This is the second part of the analysis. In the first part (see
../input/PHO5-data/20231019-pool-qc-PHO5.Rmd), I did QC and
exported the filtered dataset. Here, I will continue working with that
dataset and answer our biological questions.
Data
Import the background subtracted data
dat0 <- read_tsv("../input/20231023-PHO5-bg-subtracted-data.tsv", col_types = "ccccdddddc")
Filter the data
dat <- filter(dat0, host != "PHO84", flag == "pass", date != "02/10") %>%
# based on previous QC, the following sample (both replicates) have high
# variance - one biological replicate is highly expressed, while the other
# two have mNeon, but barely any RFP expression.
mutate(flag = ifelse(plasmid == "233" & host == "pho2∆", "high.var", flag))
Chimera makeup information
meta <- read_tsv("../input/20230208-chimera-Pho4-makeup.txt", col_types = "ccccc")
Summarize data
Here we would like calculate the ratio of RFP/GFP for each chimera
(plasmid) across all replicates, including from different days. Note
that the parameter of interest is a ratio, which can be estimated using
either “means of ratios” or “ratios of means”. These are just two
specific instances of a more general estimator, representing two choices
of the weights. The “means of ratios” first calculates the ratios for
each replicate within a plasmid, then average them. In this calculation,
each replicate is given the weight of 1/n (equal). The “ratios of means”
first sum up the GFP and RFP values separately across the replicates for
each plasmid, then take the ratio between them. In this estimator, the
weight for each replicate is x / sum(x), where x is the denominator in
the ratio, i.e., GFP. In other words, this estimator will give more
weights to the replicates where the chimera had a higher expression
level.
Both estimators are known to be biased. We will ignore that for the
moment. In terms of a choice between the two, it seems that there is no
reason to give more weights to the experiments with a higher GFP signal.
So, the “means of ratios” seems a more natural choice. However, we will
calcultae both and dedice later.
A final question is how to calculate the variance of the ratio
estimate. According to the survey package manual,
an approximate estimator for the variance is
\[
r = \frac{\bar{y}}{\bar{x}}, \text{where}\
\bar{y}=\frac{1}{n}\sum_{i=1}^{n}y_i\ \text{and}\
\bar{x}=\frac{1}{n}\sum_{i=1}^{n}x_i\ \\
\hat{V}(r) = (1-\frac{n}{N})(\frac{1}{\bar{x}^2})\frac{s_r^2}{n}\
\text{where}\ s_r^2=\frac{1}{n-1}\sum_{i=1}^{n}(y_i-rx_i)^2
\]
Assuming that N>>n, we can ignore the first term in the
variance estimator. The rest can be calculated from the data
datsum <- dat %>%
filter(!is.na(plasmid)) %>%
group_by(plasmid, host) %>%
summarize(
n = n(),
mG = mean(BL1.H),
mR = mean(YL2.H),
A = mean(YL2.H/BL1.H),
r = mR/mG,
s2 = 1/(n-1)*sum((YL2.H - r*BL1.H)^2),
vr = 1/(mG^2)*s2/n,
se = sqrt(vr),
.groups = "drop"
) %>%
select(-s2, -vr)# %>%
#pivot_wider(names_from = host, values_from = BL1.H:`nR/G`) %>%
#mutate(`pho2∆/PHO2` = `R/G_pho2∆`/`R/G_PHO2`,
# `n.pho2∆/PHO2` = `nR/G_pho2∆`/`nR/G_PHO2`)
For each chimera, we would also like to calculate three
values:
- A in pho2∆: this is its base activity without Pho2
- A in PHO2: this is its full activity with Pho2
- A_PHO2 / A_pho2∆: this is the Pho2 enhancement of activity
We assign the chimeras into several groups, based on their A_PHO2 and
A_PHO2/A_pho2∆
ximera <- datsum %>%
mutate(host = fct_recode(host, "pho2" = "pho2∆")) %>%
pivot_wider(id_cols = plasmid, names_from = host, values_from = A, names_prefix = "A_") %>%
mutate(
s_PHO2 = A_PHO2 / A_PHO2[plasmid == "194"],
s_pho2 = A_pho2 / A_pho2[plasmid == "194"],
boost = A_PHO2 / A_pho2,
group = case_when(
plasmid %in% c("188", "194") ~ "ref",
s_PHO2 < 0.2 ~ "n.f.",
.default = "chimera"
),
group = fct_relevel(group, "ref", "chimera", "n.f.")
) %>%
left_join(select(meta, plasmid, set, symbol), by = "plasmid") %>%
mutate(symbol = fct_reorder(symbol, s_PHO2, .desc = TRUE)) %>%
relocate(c(set, symbol, group), .after = plasmid)
Export the summarized data
write_tsv(ximera, file = "../output/20231125-PHO5pr-chimera-summarized.tsv")
Data selection
Write a function that can select a subset of the chimeras given a set
of rules
my_data_select <- function(pattern = NULL, Set = NULL){
# change region 4 into a consistent format
tmp <- ximera %>%
mutate(
symbol = as.character(symbol),
Symbol = ifelse(
nchar(symbol) == 5,
paste0(str_sub(symbol, 1, 3),
str_sub(symbol, 4, 4),
str_sub(symbol, 4, 4),
str_sub(symbol, 5, 5)),
symbol
)) %>%
select(plasmid, Symbol)
# starting set
if(length(Set) == 0)
xim <- filter(tmp, !plasmid %in% refs)
else
xim <- filter(tmp, set %in% Set, !plasmid %in% refs)
# compare to the pattern
try(if(nchar(pattern) != 6) stop("Pattern must be a string with 6 characters"))
symbols = xim$Symbol # extrac the symbols for testing
include = nchar(symbols) > 0 # initialize the inclusion vector
for(i in 1:6){
p = substr(pattern, i, i)
if(p != "X" & p != "x"){ # ignore X and x
test = toupper(str_sub(symbols, i, i)) == toupper(p)
include = include & test
}
}
select <- cbind(xim, include)
return(select$plasmid[select$include])
}
my_data_select_m <- function(patterns = NULL, Set = NULL){
all_selected = c()
try(if(length(patterns) == 0) stop("No patterns provided"))
for(i in patterns)
all_selected = c(all_selected, my_data_select(pattern = i))
return(unique(all_selected))
}
Analysis
Plotting functions
Set up common parameters for thresholding and plotting
# reference Pho4 plasmid ids
refs <- c("188", "194")
# colors
date.colors = c(brewer.pal(name="Dark2", n = 8), brewer.pal(name="Paired", n = 8))
host.colors = c("PHO2" = "gray30", "pho2∆" = "gray70")
point.colors = c("PHO2" = "forestgreen", "pho2∆" = "purple4")
#
Data prep and transform
my_data_prep <- function(selection){
# given a selection of chimera ID (plasmid), prepare a data frame for plotting
# subset data
tmp <- ximera %>%
filter(plasmid %in% c(refs, selection)) %>%
select(plasmid, symbol, group) %>%
inner_join(dat, by = "plasmid") %>%
return(tmp)
}
Plot RFP/GFP ratio and individual components
my_plot_ratio <- function(selection){
tmp <- my_data_prep(selection)
p <- tmp %>%
select(-c(FSC.H, nGFP, nRFP, flag)) %>%
mutate(`R/G` = YL2.H/BL1.H) %>%
pivot_longer(cols = c(BL1.H, YL2.H, `R/G`),
names_to = "parameter", values_to = "value") %>%
mutate(parameter = factor(parameter, levels = c("R/G", "YL2.H", "BL1.H"),
labels = c("RFP/GFP", "PHO5pRFP", "Pho4-GFP"))) %>%
ggplot(aes(x = symbol, y = value, group = host)) +
stat_summary(aes(group = host), fun.data = "mean_cl_boot", geom = "errorbar",
position = position_dodge(0.5), width = 0.3) +
geom_bar(aes(fill = host), width = 0.5, alpha = 0.8,
stat = "summary", fun = "mean", position = position_dodge(0.5)) +
geom_point(data = function(x) subset(x, !symbol %in% c("CCCCC", "SSSSS")),
aes(group = host, color = host), size = 1, shape = 3, alpha = 0.9,
position = position_jitterdodge(dodge.width = 0.5, jitter.width = 0.1)) +
scale_color_manual(values = point.colors) +
scale_fill_manual(values = host.colors) +
facet_grid(parameter~group, scales = "free", space = "free_x") +
theme_bw(base_size = 18) + background_grid(minor = "none") +
xlab("Pho4 chimera") +
theme(axis.text.x = element_text(angle = 30, hjust = 1, family = "mono"),
legend.position = "top",
axis.title = element_blank())
return(p)
}
Plot chimera relative activity to ScPho4 w/ Pho2 and boost factor
my_plot_rel_act <- function(selection){
# given a selection of chimera IDs, plot their functionality w/PHO2
# relative to ScPho4, and their boost
p <- filter(ximera, plasmid %in% c(refs, selection)) %>%
ggplot(aes(x = symbol, y = s_PHO2)) +
geom_col(width = 0.3, color = "black", fill = "gray80") +
geom_hline(yintercept = 1, linetype = 2, color = "gray30") +
facet_grid(.~group, scales = "free_x", space = "free_x") +
scale_y_continuous(labels = scales::percent) +
xlab("Pho4 chimera") + ylab("A<sub>PHO2</sub>, chimera/ScPho4") +
ggtitle("Chimera function in <em>S. cerevisiae</em>") +
theme_bw(base_size = 18) + background_grid(minor = "none") +
theme(axis.text.x = element_text(angle = 30, hjust = 1, family = "mono"),
#strip.text = element_blank(),
plot.title = element_markdown(hjust = 0.5),
axis.title.y = element_markdown(),
legend.position = "top")
return(p)
}
my_plot_boost <- function(selection){
# given a selection of chimera IDs, plot their functionality w/PHO2
# relative to ScPho4, and their boost
# dat
tmp <- filter(ximera, plasmid %in% c(refs, selection)) %>%
mutate(perc_pho2 = A_pho2/A_PHO2) %>%
pivot_longer(cols = c(s_PHO2, boost, perc_pho2),
names_to = "parameter", values_to = "ratio")
# labeller
par.explain <- c(
s_PHO2 = "Rel. A<sub>PHO2</sub>",
boost = "Boost",
perc_pho2 = "%A<sub>pho2∆</sub>"
)
p <- ggplot(tmp, aes(x = symbol, y = ratio)) +
geom_col(width = 0.3, color = "black", fill = "gray80") +
geom_hline(yintercept = 1, linetype = 2, color = "gray30") +
facet_grid(parameter~group, scales = "free", space = "free_x",
labeller = labeller(parameter = par.explain)) +
theme_bw(base_size = 18) +
background_grid(minor = "none") +
theme(axis.text.x = element_text(angle = 30, hjust = 1, family = "mono"),
axis.title.x = element_blank(),
strip.text.y = element_markdown(size = rel(0.9))
#plot.title = element_text(hjust = 0.5)
)
return(p)
}
High variance samples
Summarize the background subtracted data by calculating the means and
cv for each strain.
cv <- dat %>%
select(-nGFP, -nRFP) %>%
pivot_longer(FSC.H:YL2.H, names_to = "parameter", values_to = "intensity") %>%
group_by(date, plasmid, host, parameter) %>%
summarize(
n = n(),
mean = mean(intensity),
cv = sd(intensity)/mean(intensity),
.groups = "drop"
) %>%
arrange(desc(cv))
Number of replicates left for each sample
expt <- dat %>%
filter(host %in% c("PHO2", "pho2∆"), !plasmid %in% c("188", "194")) %>%
group_by(date, plasmid, host) %>%
summarize(n = n(), .groups = "drop")
expt %>%
ggplot(aes(x = plasmid, y = n)) +
geom_col(aes(fill = host)) +
facet_grid(date ~ .) +
scale_fill_manual(values = c("PHO2" = "gray30", "pho2∆" = "gray70")) +
theme_minimal() + background_grid(major = "none") + panel_border(size = 0.5) +
scale_y_continuous(name = "Replicates", breaks = c(6)) + xlab(NULL) +
theme(axis.text.x = element_text(angle = 90),
strip.text.y = element_text(angle = 0),
legend.position = "top")

Use the control strain (pH194 with PHO2) to identify and correct for
systematic biases
control <- filter(dat, plasmid == "194", host == "PHO2") %>%
separate(well, into = c("row", "col"), sep = 1) %>%
droplevels()
Model for mNeon
gfp.model.0 <- lm(BL1.H ~ log10(events) + date + row*col, data = control)
step(gfp.model.0)
Start: AIC=1507.62
BL1.H ~ log10(events) + date + row * col
Df Sum of Sq RSS AIC
- row:col 6 96964 3731743 1499.4
- log10(events) 1 12751 3647531 1506.1
<none> 3634779 1507.6
- date 11 3415318 7050097 1581.0
Step: AIC=1499.41
BL1.H ~ log10(events) + date + row + col
Df Sum of Sq RSS AIC
- row 3 92309 3824052 1496.9
- log10(events) 1 9919 3741662 1497.8
<none> 3731743 1499.4
- col 2 1030496 4762240 1530.5
- date 11 3409784 7141528 1570.9
Step: AIC=1496.93
BL1.H ~ log10(events) + date + col
Df Sum of Sq RSS AIC
- log10(events) 1 16207 3840259 1495.5
<none> 3824052 1496.9
- col 2 1024530 4848582 1527.1
- date 11 3423810 7247862 1567.0
Step: AIC=1495.54
BL1.H ~ date + col
Df Sum of Sq RSS AIC
<none> 3840259 1495.5
- col 2 1068825 4909084 1526.9
- date 11 3422018 7262277 1565.3
Call:
lm(formula = BL1.H ~ date + col, data = control)
Coefficients:
(Intercept) date02/09 date02/11 date02/16 date02/18 date02/19 date02/20 date02/21 date02/22 date02/23 date03/30 date03/31 col5 col9
1827.3 -209.7 -259.9 -351.5 -242.3 -319.0 -499.0 -343.8 -233.1 -439.5 -533.6 -569.1 -107.4 -211.0
gfp.model.1 <- lm(BL1.H ~ date + col, data = control)
Model for PHO5pr::RFP
rfp.model.0 <- lm(YL2.H ~ log10(events) + date + row*col, data = control)
step(rfp.model.0)
Start: AIC=2229.67
YL2.H ~ log10(events) + date + row * col
Df Sum of Sq RSS AIC
- row:col 6 13201086 560400308 2221.1
<none> 547199222 2229.7
- log10(events) 1 13838563 561037785 2231.3
- date 11 852447575 1399646797 2342.9
Step: AIC=2221.11
YL2.H ~ log10(events) + date + row + col
Df Sum of Sq RSS AIC
<none> 560400308 2221.1
- log10(events) 1 12713396 573113704 2222.3
- col 2 65908721 626309029 2233.1
- row 3 154001100 714401408 2250.1
- date 11 851423159 1411823467 2332.2
Call:
lm(formula = YL2.H ~ log10(events) + date + row + col, data = control)
Coefficients:
(Intercept) log10(events) date02/09 date02/11 date02/16 date02/18 date02/19 date02/20 date02/21 date02/22 date02/23 date03/30
19089.8 3496.2 -6513.6 -4709.4 -6221.7 -3241.9 -6452.8 -4558.0 -4776.0 -3995.3 -5054.2 -8507.9
date03/31 rowC rowE rowG col5 col9
-10061.8 -1882.5 -2429.8 -2641.7 -954.5 -1663.5
rfp.model.1 <- lm(YL2.H ~ log10(events) + date + row + col, data = control)
there are more systematic shifts in the RFP, significant for row,
col, date and also # of events however, I won’t be removing these
effects yet, because I’ve found that RFP/GFP ratios are pretty
consistent across days. In other words, the variation in GFP and RFP may
be cancelled out.
Check for each plasmid how consistent are the measurements between
days
tmp <- dat %>%
# remove one sample with only one valid day of experiment
filter(!(plasmid == "218" & host == "PHO2"), !plasmid %in% c("188", "194", NA)) %>%
nest(data = c(date, BL1.H, YL2.H), .by = c(plasmid, host))
day.var.gfp <- tmp %>%
mutate(model = map(data, function(df) lm(BL1.H ~ date, data = df)),
tidied = map(model, broom::tidy)) %>%
unnest(tidied) %>%
filter(term != "(Intercept)") %>%
mutate(p.adj = p.adjust(p.value, method = "BH")) %>%
select(-data, -model) %>%
filter(p.adj < 0.10) %>%
arrange(plasmid, host)
day.var.rfp <- tmp %>%
mutate(model = map(data, function(df) lm(YL2.H ~ date, data = df)),
tidied = map(model, broom::tidy)) %>%
unnest(tidied) %>%
filter(term != "(Intercept)") %>%
mutate(p.adj = p.adjust(p.value, method = "BH")) %>%
select(-data, -model) %>%
filter(p.adj < 0.10) %>%
arrange(plasmid, host)
# extract ximera names
refs <- c("188","194")
# make a test set
day.var.gfp.list <- unique(day.var.gfp$plasmid)
day.var.rfp.list <- unique(day.var.rfp$plasmid)
High day-to-day GFP variance: 212, 222, 229, 231, 241, 251, 252, 277,
301, 326, 328, 329, 331, 334 High day-to-day RFP variance: 212, 216,
239, 241
Plotting components for chimeras with high day-to-day variance in
Pho4-mNeon
p <- my_plot_ratio(c(refs,day.var.gfp.list)) +
geom_point(data = function(x) subset(x, !symbol %in% c("CCCCC", "SSSSS")),
aes(group = host, color = date), size = 1, shape = 3, alpha = 0.9,
position = position_jitterdodge(dodge.width = 0.5, jitter.width = 0.1)) +
scale_color_manual(values = date.colors, guide = "none")
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.
p

Watch out for CSCscC, SCCsS, SCCsS
Plotting components for chimeras with high day-to-day variance in
PHO5pr-mCherry
p <- my_plot_ratio(c(refs,day.var.rfp.list)) +
geom_point(data = function(x) subset(x, !symbol %in% c("CCCCC", "SSSSS")),
aes(group = host, color = date), size = 1, shape = 3, alpha = 0.9,
position = position_dodge(width = 0.5)) +
scale_color_manual(values = date.colors, guide = "none")
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.
p

most of the day-to-day variance are canceled out after RFP/GFP
normalization
All chimera, scatter plot
Design the plot
my_scatter_plot <- function(pattern){
selection = my_data_select(pattern = pattern)
scatter.colors = c("ScPho4" = "forestgreen", "CgPho4" = "blue3", "cyan2", "other" = "gray20")
names(scatter.colors)[3] = pattern
p <- ximera %>%
mutate(A_PHO2 = signif(A_PHO2, digits = 2),
A_pho2 = signif(A_pho2, digits = 2),
group = case_when(
symbol == "CCCCC" ~ "CgPho4",
symbol == "SSSSS" ~ "ScPho4",
plasmid %in% selection ~ pattern,
.default = "other"
),
group = fct_relevel(group, names(scatter.colors))) %>%
ggplot(aes(x = A_PHO2, y = A_pho2, label = symbol)) +
geom_point(aes(color = group), size = 2.5) +
scale_color_manual("Pho4 type", values = scatter.colors) +
geom_abline(slope = 1) +
theme_gray(base_size = 14)
return(p)
}
my_scatter_plot_fix <- function(){
# this function is the same as above, but is used to plot region 4
# effects alone, and doesn't take any input
s1 = my_data_select(pattern = "XXXCCX")
s2 = my_data_select(pattern = "XXXSSX")
scatter.colors = c("ScPho4" = "forestgreen", "CgPho4" = "blue3",
"P2ID:Cg" = "cyan2", "P2ID:Sc" = "palegreen",
"P2ID:mixed" = "gray20")
p <- ximera %>%
mutate(A_PHO2 = signif(A_PHO2, digits = 2),
A_pho2 = signif(A_pho2, digits = 2),
group = case_when(
symbol == "CCCCC" ~ "CgPho4",
symbol == "SSSSS" ~ "ScPho4",
plasmid %in% s1 ~ "P2ID:Cg",
plasmid %in% s2 ~ "P2ID:Sc",
.default = "P2ID:mixed"
),
group = fct_relevel(group, names(scatter.colors))) %>%
ggplot(aes(x = A_PHO2, y = A_pho2, label = symbol)) +
geom_abline(slope = 1) +
geom_point(aes(color = group), size = 2.5) +
scale_color_manual(NULL, values = scatter.colors) +
labs(x = bquote(A[PHO2]), y = bquote(A[pho2])) +
theme_minimal(base_size = 16) +
theme(legend.text = element_text(size = rel(0.75)))
return(p)
}
p <- my_scatter_plot_fix()
ggsave(filename = "../img/20231220-all-chimera-scatter-color-by-P2ID.png",
plot = p, width = 6, height = 4, dpi = 300)
ggplotly(p + labs(x = "A<sub>PHO2</sub>", y = "A<sub>pho2</sub>") +
theme_gray(base_size = 16) +
theme(legend.text = element_markdown()),
tooltip = c("label", "x", "y"))
ANOVA
split <- c(1,1,1,1,1); names(split) <- paste0("P", 1:5)
tmp <- ximera %>%
filter(set == "M", group != "n.f.") %>%
separate_wider_position(symbol, split) %>%
mutate(across(P1:P5, ~factor(.x, levels = c("S", "C"))))
lm <- lm(A_pho2 ~ (P1+P2+P3+P4+P5), data = tmp)
summary(lm)
Call:
lm(formula = A_pho2 ~ (P1 + P2 + P3 + P4 + P5), data = tmp)
Residuals:
Min 1Q Median 3Q Max
-3.5062 -1.1430 -0.0082 0.8638 6.5285
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.7264 1.0811 -0.672 0.5102
P1C 2.3316 0.8687 2.684 0.0152 *
P2C 0.7880 0.8687 0.907 0.3763
P3C 2.5260 0.9010 2.804 0.0117 *
P4C 4.0647 0.9792 4.151 0.0006 ***
P5C 1.2254 0.9010 1.360 0.1906
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 2.086 on 18 degrees of freedom
Multiple R-squared: 0.7398, Adjusted R-squared: 0.6675
F-statistic: 10.23 on 5 and 18 DF, p-value: 9.054e-05
The main effects were calculated by averaging over all chimeras with
CgPho4 region at the respective position. I’d like to break them down by
backgrounds. For example, for region 3, I’d like to see the pairwise
comparisons between CCCSS and CCSSS, where only region 3 differs. The
steps are
- select the region to be compared. split the symbol into two parts -
the genotype of the focal region and the rest
- group by the second part (rest) and calculate the differential
my_calc_region_effect <- function(region, variable){
# this function takes the name of a variable of interest
# x specifies the foreground region, which will be examined for its effect on
# the variable of interest.
# it then transforms the ximera data frame to preserve only the variable of
# interest, pivots it wider after grouping by the background composition.
# prepare the data by mutating the symbol column into fg and bg
valid.var <- c("A_PHO2", "A_pho2", "s_PHO2", "spho2", "boost")
if(!variable %in% valid.var)
stop(paste0("Please specify one of the valid variable names:",
paste(valid.var, collapse = ", ")))
tmp <- ximera %>%
filter(set == "M") %>%
select(plasmid, symbol, var = {{ variable }}) %>%
mutate(fg = str_sub(symbol, region, region) %>% toupper(),
bg = symbol %>% toupper())
# replace the foreground region with X for grouping
str_sub(tmp$bg, region, region) <- "X"
# reorganize the tibble for easier handling, optional
tmp <- relocate(tmp, fg, bg, .before = symbol) %>% select(-symbol)
# pivot the data into a wide format such that for each background, there
# are two values for the variable of interest, one from the chimera with
# CgPho4's version in the foreground and another with ScPho4's version
tmp <- tmp %>%
select(plasmid, fg, bg, var) %>%
pivot_wider(id_cols = bg, names_from = "fg",
values_from = c(plasmid, var)) %>%
unite(plasmid, starts_with("plasmid")) %>%
mutate(label = paste(bg, plasmid, sep = "\n"))
return(tmp)
}
my_plot_region_effect_onevar <- function(region, variable){
# this function uses `my_calc_region_effect` output as the data
# and makes a xy scatter plot, where x shows the value of the variable of
# interest with CgPho4 in the focal region, and y for the ScPho4 version
tmp <- my_calc_region_effect(region, variable)
p <- ggplot(tmp, aes(x = var_C, y = var_S, label = label)) +
geom_point(size = 2.5) +
geom_abline(slope = 1) +
xlab(paste0("Region ", region, " from CgPho4")) +
ylab(paste0("Region ", region, " from ScPho4")) +
xlim(0, NA) + ylim(0, NA) +
ggtitle(paste0("Effect on ", variable)) +
theme_gray(base_size = 16) +
theme(plot.title = element_text(hjust = 0.5))
return(p)
}
x = 5
p1 <- my_plot_region_effect_onevar(x, "A_PHO2")
p2 <- my_plot_region_effect_onevar(x, "A_pho2")
subplot(p1, p2, margin = 0.05) %>%
layout(title = paste("Region", x, "swap effect on A_PHO2 and A_pho2", sep = " "),
xaxis = list(title = paste0("Region ", x, " from CgPho4")),
yaxis = list(title = paste0("Region ", x, " from ScPho4")) )
Here, I’d like to take what I build above and create a new tibble, in
which each row is a different background (makeup of the chimera except
for the focal region). The value columns are:
- dA_PHO2 = A_PHO2_Cg - A_PHO2_Sc
- dA_pho2 = A_pho2_Cg - A_pho2_Sc
- A_PHO2_Sc = A_PHO2_Sc
The goal is to plot dA_PHO2 and dA_pho2 side-by-side for each
background.
my_comp_region_effect <- function(region){
# this function uses my_calc_region_effect to get the value for the variable of interest
# with either Cg or Sc version in the focal region, separately for each background composition
# it does so for two variables, A_PHO2 and A_pho2, then calculate dA_PHO2, dA_pho2, and
# combine them
PHO2 = my_calc_region_effect(region, "A_PHO2") %>%
mutate(dA_PHO2 = var_C - var_S,
# mean A_PHO2
M_PHO2 = (var_S + var_C)/2,
NF = ifelse(M_PHO2 <=3.5, TRUE, FALSE)) %>%
select(-var_S, -var_C)
pho2 = my_calc_region_effect(region, "A_pho2") %>%
mutate(dA_pho2 = var_C - var_S,
M_pho2 = (var_S + var_C)/2) %>%
select(-var_S, -var_C)
dat <- full_join(PHO2, pho2, by = c("bg", "plasmid", "label")) %>%
select(bg, plasmid, dA_PHO2, dA_pho2, M_PHO2, M_pho2, NF)
return(dat)
}
my_plot_region_effect_twovar_line <- function(region, highlight = "none"){
# this function uses my_comp_region_effect to generate the data
# and plot the difference in A_PHO2 and A_pho2 between the CgPho4 vs ScPho4
# in the focal region
dat <- my_comp_region_effect(region) %>%
pivot_longer(cols = c(dA_PHO2, dA_pho2),
names_to = "host", values_to = "diff") %>%
mutate(host = fct_recode(host, `PHO2` = "dA_PHO2", `pho2∆` = "dA_pho2"),
host = fct_relevel(host, "PHO2"))
if(highlight != "none" & highlight != region){
hl = as.numeric(highlight)
dat <- mutate(dat, grp = str_sub(bg, hl, hl) %>% toupper(),
grp = fct_recode(grp, CgPho4 = "C", ScPho4 = "S"))
}else{
dat <- mutate(dat, grp = ifelse(NF, "n.f.", "others"))
}
# specify legend title
legend.title = ""
if(highlight != "none" & highlight != region){
hl = as.numeric(highlight)
dat <- mutate(dat, grp = str_sub(bg, hl, hl) %>% toupper(),
grp = fct_recode(grp, CgPho4 = "C", ScPho4 = "S"))
legend.title = paste("Region", highlight, sep = " ")
}else{
dat <- mutate(dat, grp = ifelse(NF, "no", "yes"))
legend.title = "Functional"
}
# specify arrow annotation
arrow.x = 0.7
arrow.y = (max(dat$diff) - min(dat$diff)) / 5
p <- dat %>%
ggplot(aes(x = host, y = diff, label = bg)) +
geom_point(aes(color = grp), size = 2, alpha = 0.8,
position = position_jitter(0.05)) +
geom_line(aes(group = bg), linewidth = 0.2, alpha = 0.8) +
geom_segment(aes(x = arrow.x, xend = arrow.x, y = -arrow.y, yend = arrow.y),
arrow = arrow(length = unit(0.03, "npc"), ends = "both"),
color = "gray60", lwd = 1, alpha = 0.5) +
geom_segment(aes(x = arrow.x - 0.05, xend = arrow.x + 0.05, y = 0, yend = 0),
lwd = 2, color = "gray60") +
annotate("text", x = arrow.x - 0.1, y = 5, label = "CgPho4++",
angle = '90', color = "gray30") +
annotate("text", x = arrow.x + 0.1, y = -5, label = "ScPho4++",
angle = '270', color = "gray30") +
scale_color_manual(legend.title, values = c("orange", "gray20")) +
ylab("Region swap effect (Cg-Sc)") +
theme_bw(base_size = 18) +
theme(
axis.title.x = element_blank(),
axis.title.y = element_text(size = rel(0.9)),
legend.text = element_text(size = rel(0.8)),
legend.title = element_text(size = rel(0.9)),
)
return(p)
}
my_plot_region_effect_twovar_line("4", "5")# %>% ggplotly()
ggsave("../img/20231221-region-swap-effect-4-on-5.png", width = 6, height = 4, dpi = 150)

my_plot_region_effect_twovar_line("5", "4")# %>% ggplotly()
ggsave("../img/20231224-region-swap-effect-5-on-4.png", width = 6, height = 4, dpi = 200)

my_plot_region_effect_twovar_line_par <- function(regions, highlight = "none"){
# this function uses my_comp_region_effect to generate the data
# and plot the difference in A_PHO2 and A_pho2 between the CgPho4 vs ScPho4
# in the focal region
dat <- map_dfr(regions, \(region) my_comp_region_effect(region), .id = "region") %>%
pivot_longer(cols = c(dA_PHO2, dA_pho2),
names_to = "host", values_to = "diff") %>%
mutate(host = fct_recode(host, `PHO2` = "dA_PHO2", `pho2∆` = "dA_pho2"),
host = fct_relevel(host, "PHO2"))
# specify legend title
legend.title = ""
if(highlight != "none" & !highlight %in% regions){
hl = as.numeric(highlight)
dat <- mutate(dat, grp = str_sub(bg, hl, hl) %>% toupper(),
grp = fct_recode(grp, CgPho4 = "C", ScPho4 = "S"))
legend.title = paste("Region", highlight, "from", sep = " ")
}else{
dat <- mutate(dat, grp = ifelse(NF, "no", "yes"))
legend.title = "Functional"
}
# specify arrow annotation
arrow.x = 0.7
arrow.y = (max(dat$diff) - min(dat$diff)) / 5
p <- dat %>%
ggplot(aes(x = host, y = diff, label = bg)) +
geom_point(aes(color = grp), size = 2, alpha = 0.8,
position = position_jitter(0.1)) +
geom_line(aes(group = bg), linewidth = 0.2, alpha = 0.8) +
facet_grid(grp ~ region, labeller = labeller(
grp = c(CgPho4 = "P2ID:CgPho4", ScPho4 = "P2ID:ScPho4"),
region = label_both
)) +
scale_color_manual("P2ID:", values = c("orange", "gray20")) +
ylab("Region swap effect (Cg-Sc)") +
theme_bw(base_size = 18) +
theme(
axis.title.x = element_blank(),
axis.title.y = element_text(size = rel(0.9)),
legend.text = element_text(size = rel(0.8)),
legend.title = element_text(size = rel(0.9)),
)
return(p)
}
my_plot_region_effect_twovar_line_par(c(1,2,3), "4")
ggsave("../img/20231224-region-swap-effect-1to3-on-4.png")
Saving 7.29 x 4.51 in image

my_plot_region_effect_twovar_side <- function(region){
# this function uses my_comp_region_effect to generate the data
# and plot the difference in A_PHO2 and A_pho2 between the CgPho4 vs ScPho4
# in the focal region
dat <- my_comp_region_effect(region) %>%
pivot_longer(cols = c(dA_PHO2, dA_pho2),
names_to = "host", values_to = "diff") %>%
mutate(host = fct_recode(host, `PHO2` = "dA_PHO2", `pho2∆` = "dA_pho2"),
host = fct_relevel(host, "PHO2"))
p <- dat %>%
ggplot(aes(x = bg, y = diff, group = host)) +
geom_col(aes(fill = host), position = position_dodge(0.9)) +
scale_fill_manual(values = host.colors) +
ylab("Region swap diff (Cg vs Sc)") +
theme_cowplot(font_size = 20) +
panel_border(color = "gray30") +
background_grid(major = "y", minor = "none") +
theme(axis.text.x = element_text(angle = 45, hjust = 1, family = "courier"),
axis.title.x = element_blank(),
legend.position = "top")
return(p)
}
Triangle heatmap
First, write a function to generate the data for plotting. If we are
going to use ggplot, we need a tibble to store the data, something in
the following form
| 209 |
CCSCC |
3 |
3 |
8.25 |
7.82 |
0.468 |
1.06 |
0.94 |
If we are ok with using non ggplot - heatmaps are not ggplot’s
strength anyways - we can just build a matrix.
Note that this way of summarizing the data has many limitaitons: 1)
it requires specifying the reference, either CCCCC or SSSSS. Everything
is measured against that; 2) it only shows pairwise (two region)
interactions. This turns out to be fine with five regions, since every
chimera can be expressed as either a 0, 1 or 2 region swap from one of
the two reference genotypes. With 6 or more regions, higher level (3 or
more region) interactions cannot be visualized this way. Because of
this, we will focus on just the main set for this analysis.
To build the matrix, we need to first identify the chimeras that
belong to the set. For that, we will use the “main” set, with the five
region split, for the moment at least. The function will first determine
which reference to use. If we use SSSSS as the reference, for example,
we will assign 0 to the reference. All other chimeras with 1 or 2
regions from Cg will be used to fill an upper triangular matrix, using
one of the values of interest, e.g., A_PHO2.
my_upper_triangular_mat <- function(alt = "C", var = "A_PHO2", nf.as.na = F){
# given the alternative allele (C/S) and a variable of interest, e.g., A_PHO2,
# output an upper triangular matrix containing the values from the variable
# of interest, with the row and col numbers based on the first and second
# positions containing the alternative allele. If all positions contain the
# reference allele, the value is subtracted from all values in the matrix
# when just one position is the alternative allele, the value in the diagonal
# is set. when there are more than 2 regions containing the alternative allele
# skip.
# if "nf.as.na = TRUE", evaluate if the activity of either of the two chimeras
# being compared is non functional. if yes, set the corresponding matrix value
# to NA
out_mat <- matrix(NA, nrow = 5, ncol = 5)
ref_val <- NA
dat <- filter(ximera, set == "M") %>%
mutate(S = as.character(symbol) %>% toupper())
if(nf.as.na){
dat <- filter(dat, group != "n.f.")
}
for(i in seq(1, nrow(dat))){
symbol = dat[i, "S"]
# determine which positions contain the alternative allele
p = str_locate_all(symbol, alt)[[1]][,"start"]
l = length(p) # how many positions contain the alt allele
v = dat[[var]][i] # retrieve the value of the variable
if(l == 0)
ref_val = v
else if(l == 1)
out_mat[p, p] = v
else if(l == 2)
out_mat[p[1], p[2]] = v
}
out_mat = out_mat - ref_val
return(out_mat)
}
my_combined_triangular_mat <- function(alt = "C"){
# given the alternative allele (C/S), output a matrix containing the values
# for both with and without Pho2, arranged in two complementary triagular
# matrices, with the row and col numbers based on the first and second
# positions containing the alternative allele. If all positions contain the
# reference allele, the value is subtracted from all values in the matrix
# when just one position is the alternative allele, the value in the diagonal
# is set. when there are more than 2 regions containing the alternative allele
# skip.
out_mat <- matrix(NA, nrow = 6, ncol = 6)
upper <- cbind(NA, my_upper_triangular_mat(alt, var = "A_PHO2", )) %>%
rbind(., NA)
lower <- rbind(NA, t(my_upper_triangular_mat(alt, var = "A_pho2"))) %>%
cbind(., NA)
out_mat = ifelse(is.na(upper), lower, upper)
return(out_mat)
}
my_plot_triangle_heatmap <- function(alt, var){
# this function takes the output of the function above and makes a heatmap
# using pheatmap function, then rotates it using grid graphics
# thanks to https://bookdown.org/rdpeng/RProgDA/the-grid-package.html#grid-graphics-coordinate-systems
# adding title based on https://davetang.github.io/muse/pheatmap.html
# construct title of plot
ref = ifelse(alt == "C", "ScPho4", "CgPho4")
bg = ifelse(var == "A_PHO2", "with PHO2", "w/o pho2")
my_title <- paste("Epistasis between regions on", ref, "background", bg)
test <- my_upper_triangular_mat(alt = alt, var = var)
paletteLength = 50
myColors <- colorRampPalette(c("steelblue4", "gray90", "red"))(paletteLength)
rng <- max(abs(test), na.rm = TRUE)
myBreaks <- c(seq(-rng, 0, length.out=ceiling(paletteLength/2) + 1),
seq(rng/paletteLength, rng,
length.out=floor(paletteLength/2)))
p <- pheatmap::pheatmap(test, color = myColors, breaks = myBreaks,
border_color = NA, na_col = NA, silent = TRUE,
cluster_cols = FALSE, cluster_rows = FALSE)
vp <- viewport(x = 0.5, y = 0.25,
width = unit(4.5, "in"), height = unit(4.5, "in"), angle = 47)
grid.newpage()
pushViewport(vp)
grid.draw(p$gtable)
popViewport()
grid.text(label = my_title, x = 0.5, y = 0.95, gp = gpar(fontsize = 16, fontface = "bold"))
return(p)
}
my_plot_combined_triangle_heatmap <- function(alt){
# this function takes the output of the function my_combined_triangular_mat()
# using pheatmap function, then rotates it using grid graphics
# thanks to https://bookdown.org/rdpeng/RProgDA/the-grid-package.html#grid-graphics-coordinate-systems
# adding title based on https://davetang.github.io/muse/pheatmap.html
# construct title of plot
ref = ifelse(alt == "C", "ScPho4", "CgPho4")
my_title <- paste("Epistasis between regions on", ref, "background")
test <- my_combined_triangular_mat(alt = alt)
paletteLength = 50
myColors <- colorRampPalette(c("steelblue4", "gray90", "red"))(paletteLength)
rng <- max(abs(test), na.rm = TRUE)
myBreaks <- c(seq(-rng, 0, length.out=ceiling(paletteLength/2) + 1),
seq(rng/paletteLength, rng,
length.out=floor(paletteLength/2)))
p <- pheatmap::pheatmap(test, color = myColors, breaks = myBreaks,
border_color = NA, na_col = NA, silent = TRUE,
cluster_cols = FALSE, cluster_rows = FALSE)
vp <- viewport(x = 0.5, y = 0.45,
width = unit(3, "in"), height = unit(2.8, "in"), angle = 47)
grid.newpage()
pushViewport(vp)
grid.draw(p$gtable)
popViewport()
grid.text(label = my_title, x = 0.5, y = 0.95,
gp = gpar(fontsize = 16, fontface = "bold"))
grid.text(label = "With Pho2", x = 0.1, y = 0.65, just = c("left", "top"),
gp = gpar(fontsize = 14, fontface = "bold"))
grid.text(label = "Without pho2", x = 0.1, y = 0.25, just = c("left", "top"),
gp = gpar(fontsize = 14, fontface = "bold"))
return(p)
}
p1 <- my_plot_combined_triangle_heatmap("C")

p2 <- my_plot_combined_triangle_heatmap("S")

Region 4 splits
We have so far focused on the main set with the 5 region design. In
the scatter plot above, we see that there is a subset of chimeras in
between the P2ID:Sc and P2ID:Cg ones. They are interesting in that their
A_pho2∆/A_PHO2 ratios are intermediate.
Below, we will zoom in onto this set and try to
understand what they tell us.
my_plot_boost(selection = ximera$plasmid[ximera$set == "S"])

my_plot_ratio(selection = ximera$plasmid[ximera$set == "S"])

LS0tCnRpdGxlOiAiRTAxMyBQaG80IGNoaW1lcmEgYWN0aXZpdHkgYW5hbHlzaXMgdXNpbmcgUEhPNSByZXBvcnRlciwgYW5hbHlzaXMiCmF1dGhvcjogIkJpbiBIZSIKZGF0ZTogIjIwMjMtMTAtMzEgdXBkYXRlZCBgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpyZXF1aXJlKHBsb3RseSkKcmVxdWlyZSh0aWR5dmVyc2UpCnJlcXVpcmUoZ2dyaWRnZXMpCnJlcXVpcmUoY293cGxvdCkKcmVxdWlyZShSQ29sb3JCcmV3ZXIpCnJlcXVpcmUoZ3JpZCkKcmVxdWlyZShnZ3RleHQpCmBgYAoKYGBge3J9Cm9sZCA8LSB0aGVtZV9zZXQodGhlbWVfYncoYmFzZV9zaXplID0gMTYpKQpgYGAKClRoaXMgaXMgdGhlIHNlY29uZCBwYXJ0IG9mIHRoZSBhbmFseXNpcy4gSW4gdGhlIGZpcnN0IHBhcnQgKHNlZSBgLi4vaW5wdXQvUEhPNS1kYXRhLzIwMjMxMDE5LXBvb2wtcWMtUEhPNS5SbWRgKSwgSSBkaWQgUUMgYW5kIGV4cG9ydGVkIHRoZSBmaWx0ZXJlZCBkYXRhc2V0LiBIZXJlLCBJIHdpbGwgY29udGludWUgd29ya2luZyB3aXRoIHRoYXQgZGF0YXNldCBhbmQgYW5zd2VyIG91ciBiaW9sb2dpY2FsIHF1ZXN0aW9ucy4KCiMgR29hbAotIEFuYWx5emUgdGhlIGZ1bGwgY2hpbWVyYSBzZXQgZmxvdyByZXN1bHRzIGZvciBfUEhPNXByXy1tQ2hlcnJ5IHJlcG9ydGVyLgotIERldmVsb3AgYW4gYW5hbHlzaXMgcGlwZWxpbmUgdG8gcGVyZm9ybSBRQywgY29ycmVjdGlvbiAoaWYgbmVlZGVkKSBhbmQgcGxvdHRpbmcgdGhlIHJlc3VsdHMuCgojIERhdGEKSW1wb3J0IHRoZSBiYWNrZ3JvdW5kIHN1YnRyYWN0ZWQgZGF0YQpgYGB7cn0KZGF0MCA8LSByZWFkX3RzdigiLi4vaW5wdXQvMjAyMzEwMjMtUEhPNS1iZy1zdWJ0cmFjdGVkLWRhdGEudHN2IiwgY29sX3R5cGVzID0gImNjY2NkZGRkZGMiKQpgYGAKCkZpbHRlciB0aGUgZGF0YQpgYGB7cn0KZGF0IDwtIGZpbHRlcihkYXQwLCBob3N0ICE9ICJQSE84NCIsIGZsYWcgPT0gInBhc3MiLCBkYXRlICE9ICIwMi8xMCIpICU+JSAKICAjIGJhc2VkIG9uIHByZXZpb3VzIFFDLCB0aGUgZm9sbG93aW5nIHNhbXBsZSAoYm90aCByZXBsaWNhdGVzKSBoYXZlIGhpZ2gKICAjIHZhcmlhbmNlIC0gb25lIGJpb2xvZ2ljYWwgcmVwbGljYXRlIGlzIGhpZ2hseSBleHByZXNzZWQsIHdoaWxlIHRoZSBvdGhlciAKICAjIHR3byBoYXZlIG1OZW9uLCBidXQgYmFyZWx5IGFueSBSRlAgZXhwcmVzc2lvbi4KICBtdXRhdGUoZmxhZyA9IGlmZWxzZShwbGFzbWlkID09ICIyMzMiICYgaG9zdCA9PSAicGhvMuKIhiIsICJoaWdoLnZhciIsIGZsYWcpKQpgYGAKCkNoaW1lcmEgbWFrZXVwIGluZm9ybWF0aW9uCmBgYHtyfQptZXRhIDwtIHJlYWRfdHN2KCIuLi9pbnB1dC8yMDIzMDIwOC1jaGltZXJhLVBobzQtbWFrZXVwLnR4dCIsIGNvbF90eXBlcyA9ICJjY2NjYyIpCmBgYAoKIyMgU3VtbWFyaXplIGRhdGEKCkhlcmUgd2Ugd291bGQgbGlrZSBjYWxjdWxhdGUgdGhlIHJhdGlvIG9mIFJGUC9HRlAgZm9yIGVhY2ggY2hpbWVyYSAocGxhc21pZCkgYWNyb3NzIGFsbCByZXBsaWNhdGVzLCBpbmNsdWRpbmcgZnJvbSBkaWZmZXJlbnQgZGF5cy4gTm90ZSB0aGF0IHRoZSBwYXJhbWV0ZXIgb2YgaW50ZXJlc3QgaXMgYSByYXRpbywgd2hpY2ggY2FuIGJlIGVzdGltYXRlZCB1c2luZyBlaXRoZXIgIm1lYW5zIG9mIHJhdGlvcyIgb3IgInJhdGlvcyBvZiBtZWFucyIuIFRoZXNlIGFyZSBqdXN0IHR3byBzcGVjaWZpYyBpbnN0YW5jZXMgb2YgYSBtb3JlIGdlbmVyYWwgZXN0aW1hdG9yLCByZXByZXNlbnRpbmcgdHdvIGNob2ljZXMgb2YgdGhlIHdlaWdodHMuIFRoZSAibWVhbnMgb2YgcmF0aW9zIiBmaXJzdCBjYWxjdWxhdGVzIHRoZSByYXRpb3MgZm9yIGVhY2ggcmVwbGljYXRlIHdpdGhpbiBhIHBsYXNtaWQsIHRoZW4gYXZlcmFnZSB0aGVtLiBJbiB0aGlzIGNhbGN1bGF0aW9uLCBlYWNoIHJlcGxpY2F0ZSBpcyBnaXZlbiB0aGUgd2VpZ2h0IG9mIDEvbiAoZXF1YWwpLiBUaGUgInJhdGlvcyBvZiBtZWFucyIgZmlyc3Qgc3VtIHVwIHRoZSBHRlAgYW5kIFJGUCB2YWx1ZXMgc2VwYXJhdGVseSBhY3Jvc3MgdGhlIHJlcGxpY2F0ZXMgZm9yIGVhY2ggcGxhc21pZCwgdGhlbiB0YWtlIHRoZSByYXRpbyBiZXR3ZWVuIHRoZW0uIEluIHRoaXMgZXN0aW1hdG9yLCB0aGUgd2VpZ2h0IGZvciBlYWNoIHJlcGxpY2F0ZSBpcyB4IC8gc3VtKHgpLCB3aGVyZSB4IGlzIHRoZSBkZW5vbWluYXRvciBpbiB0aGUgcmF0aW8sIGkuZS4sIEdGUC4gSW4gb3RoZXIgd29yZHMsIHRoaXMgZXN0aW1hdG9yIHdpbGwgZ2l2ZSBtb3JlIHdlaWdodHMgdG8gdGhlIHJlcGxpY2F0ZXMgd2hlcmUgdGhlIGNoaW1lcmEgaGFkIGEgaGlnaGVyIGV4cHJlc3Npb24gbGV2ZWwuCgpCb3RoIGVzdGltYXRvcnMgYXJlIGtub3duIHRvIGJlIGJpYXNlZC4gV2Ugd2lsbCBpZ25vcmUgdGhhdCBmb3IgdGhlIG1vbWVudC4gSW4gdGVybXMgb2YgYSBjaG9pY2UgYmV0d2VlbiB0aGUgdHdvLCBpdCBzZWVtcyB0aGF0IHRoZXJlIGlzIG5vIHJlYXNvbiB0byBnaXZlIG1vcmUgd2VpZ2h0cyB0byB0aGUgZXhwZXJpbWVudHMgd2l0aCBhIGhpZ2hlciBHRlAgc2lnbmFsLiBTbywgdGhlICJtZWFucyBvZiByYXRpb3MiIHNlZW1zIGEgbW9yZSBuYXR1cmFsIGNob2ljZS4gSG93ZXZlciwgd2Ugd2lsbCBjYWxjdWx0YWUgYm90aCBhbmQgZGVkaWNlIGxhdGVyLgoKCkEgZmluYWwgcXVlc3Rpb24gaXMgaG93IHRvIGNhbGN1bGF0ZSB0aGUgdmFyaWFuY2Ugb2YgdGhlIHJhdGlvIGVzdGltYXRlLiBBY2NvcmRpbmcgdG8gdGhlIGBzdXJ2ZXlgIHBhY2thZ2UgW21hbnVhbF0oaHR0cHM6Ly9yc3R1ZGlvLXB1YnMtc3RhdGljLnMzLmFtYXpvbmF3cy5jb20vMTc4OTY1X2ZiNjBhMGY3YmJiNDRhNmVhMjE5NzEzZmIxYTg5YTIyLmh0bWwpLCBhbiBhcHByb3hpbWF0ZSBlc3RpbWF0b3IgZm9yIHRoZSB2YXJpYW5jZSBpcwoKJCQKciA9IFxmcmFje1xiYXJ7eX19e1xiYXJ7eH19LCBcdGV4dHt3aGVyZX1cIFxiYXJ7eX09XGZyYWN7MX17bn1cc3VtX3tpPTF9XntufXlfaVwgXHRleHR7YW5kfVwgXGJhcnt4fT1cZnJhY3sxfXtufVxzdW1fe2k9MX1ee259eF9pXCBcXApcaGF0e1Z9KHIpID0gKDEtXGZyYWN7bn17Tn0pKFxmcmFjezF9e1xiYXJ7eH1eMn0pXGZyYWN7c19yXjJ9e259XCBcdGV4dHt3aGVyZX1cIHNfcl4yPVxmcmFjezF9e24tMX1cc3VtX3tpPTF9XntufSh5X2ktcnhfaSleMgokJAoKQXNzdW1pbmcgdGhhdCBOPj5uLCB3ZSBjYW4gaWdub3JlIHRoZSBmaXJzdCB0ZXJtIGluIHRoZSB2YXJpYW5jZSBlc3RpbWF0b3IuIFRoZSByZXN0IGNhbiBiZSBjYWxjdWxhdGVkIGZyb20gdGhlIGRhdGEKCmBgYHtyfQpkYXRzdW0gPC0gZGF0ICU+JQogIGZpbHRlcighaXMubmEocGxhc21pZCkpICU+JSAKICBncm91cF9ieShwbGFzbWlkLCBob3N0KSAlPiUgCiAgc3VtbWFyaXplKAogICAgIG4gPSBuKCksCiAgICBtRyA9IG1lYW4oQkwxLkgpLAogICAgbVIgPSBtZWFuKFlMMi5IKSwKICAgICBBID0gbWVhbihZTDIuSC9CTDEuSCksCiAgICAgciA9IG1SL21HLAogICAgczIgPSAxLyhuLTEpKnN1bSgoWUwyLkggLSByKkJMMS5IKV4yKSwKICAgIHZyID0gMS8obUdeMikqczIvbiwKICAgIHNlID0gc3FydCh2ciksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKSAlPiUgCiAgc2VsZWN0KC1zMiwgLXZyKSMgJT4lIAogICNwaXZvdF93aWRlcihuYW1lc19mcm9tID0gaG9zdCwgdmFsdWVzX2Zyb20gPSBCTDEuSDpgblIvR2ApICU+JSAKICAjbXV0YXRlKGBwaG8y4oiGL1BITzJgID0gYFIvR19waG8y4oiGYC9gUi9HX1BITzJgLAogICMgICAgICAgYG4ucGhvMuKIhi9QSE8yYCA9IGBuUi9HX3BobzLiiIZgL2BuUi9HX1BITzJgKQpgYGAKCkZvciBlYWNoIGNoaW1lcmEsIHdlIHdvdWxkIGFsc28gbGlrZSB0byBjYWxjdWxhdGUgKip0aHJlZSB2YWx1ZXMqKjoKCjEuIEEgaW4gX3BobzLiiIZfOiB0aGlzIGlzIGl0cyBiYXNlIGFjdGl2aXR5IHdpdGhvdXQgUGhvMgoxLiBBIGluIF9QSE8yXzogdGhpcyBpcyBpdHMgZnVsbCBhY3Rpdml0eSB3aXRoIFBobzIKMS4gQV9QSE8yIC8gQV9waG8y4oiGOiB0aGlzIGlzIHRoZSBQaG8yIGVuaGFuY2VtZW50IG9mIGFjdGl2aXR5CgpXZSBhc3NpZ24gdGhlIGNoaW1lcmFzIGludG8gc2V2ZXJhbCBncm91cHMsIGJhc2VkIG9uIHRoZWlyIEFfUEhPMiBhbmQgQV9QSE8yL0FfcGhvMuKIhgoKYGBge3J9CnhpbWVyYSA8LSBkYXRzdW0gJT4lCiAgbXV0YXRlKGhvc3QgPSBmY3RfcmVjb2RlKGhvc3QsICJwaG8yIiA9ICJwaG8y4oiGIikpICU+JSAKICBwaXZvdF93aWRlcihpZF9jb2xzID0gcGxhc21pZCwgbmFtZXNfZnJvbSA9IGhvc3QsIHZhbHVlc19mcm9tID0gQSwgbmFtZXNfcHJlZml4ID0gIkFfIikgJT4lIAogIG11dGF0ZSgKICAgIHNfUEhPMiA9IEFfUEhPMiAvIEFfUEhPMltwbGFzbWlkID09ICIxOTQiXSwKICAgIHNfcGhvMiA9IEFfcGhvMiAvIEFfcGhvMltwbGFzbWlkID09ICIxOTQiXSwKICAgIGJvb3N0ID0gQV9QSE8yIC8gQV9waG8yLAogICAgZ3JvdXAgPSBjYXNlX3doZW4oCiAgICAgIHBsYXNtaWQgJWluJSBjKCIxODgiLCAiMTk0IikgfiAicmVmIiwKICAgICAgc19QSE8yIDwgMC4yICAgICAgICAgICAgICAgICB+ICJuLmYuIiwKICAgICAgLmRlZmF1bHQgPSAiY2hpbWVyYSIKICAgICksCiAgICBncm91cCA9IGZjdF9yZWxldmVsKGdyb3VwLCAicmVmIiwgImNoaW1lcmEiLCAibi5mLiIpCiAgKSAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChtZXRhLCBwbGFzbWlkLCBzZXQsIHN5bWJvbCksIGJ5ID0gInBsYXNtaWQiKSAlPiUgCiAgbXV0YXRlKHN5bWJvbCA9IGZjdF9yZW9yZGVyKHN5bWJvbCwgc19QSE8yLCAuZGVzYyA9IFRSVUUpKSAlPiUgCiAgcmVsb2NhdGUoYyhzZXQsIHN5bWJvbCwgZ3JvdXApLCAuYWZ0ZXIgPSBwbGFzbWlkKQpgYGAKCkV4cG9ydCB0aGUgc3VtbWFyaXplZCBkYXRhCmBgYHtyfQp3cml0ZV90c3YoeGltZXJhLCBmaWxlID0gIi4uL291dHB1dC8yMDIzMTEyNS1QSE81cHItY2hpbWVyYS1zdW1tYXJpemVkLnRzdiIpCmBgYAoKIyMgRGF0YSBzZWxlY3Rpb24KV3JpdGUgYSBmdW5jdGlvbiB0aGF0IGNhbiBzZWxlY3QgYSBzdWJzZXQgb2YgdGhlIGNoaW1lcmFzIGdpdmVuIGEgc2V0IG9mIHJ1bGVzCmBgYHtyfQpteV9kYXRhX3NlbGVjdCA8LSBmdW5jdGlvbihwYXR0ZXJuID0gTlVMTCwgU2V0ID0gTlVMTCl7CiAgIyBjaGFuZ2UgcmVnaW9uIDQgaW50byBhIGNvbnNpc3RlbnQgZm9ybWF0CiAgdG1wIDwtIHhpbWVyYSAlPiUgCiAgICBtdXRhdGUoCiAgICAgIHN5bWJvbCA9IGFzLmNoYXJhY3RlcihzeW1ib2wpLAogICAgICBTeW1ib2wgPSBpZmVsc2UoCiAgICAgICAgbmNoYXIoc3ltYm9sKSA9PSA1LAogICAgICAgIHBhc3RlMChzdHJfc3ViKHN5bWJvbCwgMSwgMyksIAogICAgICAgICAgICAgICBzdHJfc3ViKHN5bWJvbCwgNCwgNCksIAogICAgICAgICAgICAgICBzdHJfc3ViKHN5bWJvbCwgNCwgNCksIAogICAgICAgICAgICAgICBzdHJfc3ViKHN5bWJvbCwgNSwgNSkpLAogICAgICAgIHN5bWJvbAogICAgICApKSAlPiUgCiAgICBzZWxlY3QocGxhc21pZCwgU3ltYm9sKQogICMgc3RhcnRpbmcgc2V0CiAgaWYobGVuZ3RoKFNldCkgPT0gMCkKICAgIHhpbSA8LSBmaWx0ZXIodG1wLCAhcGxhc21pZCAlaW4lIHJlZnMpCiAgZWxzZQogICAgeGltIDwtIGZpbHRlcih0bXAsIHNldCAlaW4lIFNldCwgIXBsYXNtaWQgJWluJSByZWZzKQogICMgY29tcGFyZSB0byB0aGUgcGF0dGVybgogIHRyeShpZihuY2hhcihwYXR0ZXJuKSAhPSA2KSBzdG9wKCJQYXR0ZXJuIG11c3QgYmUgYSBzdHJpbmcgd2l0aCA2IGNoYXJhY3RlcnMiKSkKICBzeW1ib2xzID0geGltJFN5bWJvbCAjIGV4dHJhYyB0aGUgc3ltYm9scyBmb3IgdGVzdGluZwogIGluY2x1ZGUgPSBuY2hhcihzeW1ib2xzKSA+IDAgIyBpbml0aWFsaXplIHRoZSBpbmNsdXNpb24gdmVjdG9yCiAgZm9yKGkgaW4gMTo2KXsKICAgIHAgPSBzdWJzdHIocGF0dGVybiwgaSwgaSkKICAgIGlmKHAgIT0gIlgiICYgcCAhPSAieCIpeyAjIGlnbm9yZSBYIGFuZCB4CiAgICAgIHRlc3QgPSB0b3VwcGVyKHN0cl9zdWIoc3ltYm9scywgaSwgaSkpID09IHRvdXBwZXIocCkKICAgICAgaW5jbHVkZSA9IGluY2x1ZGUgJiB0ZXN0CiAgICB9CiAgfQogIHNlbGVjdCA8LSBjYmluZCh4aW0sIGluY2x1ZGUpCiAgcmV0dXJuKHNlbGVjdCRwbGFzbWlkW3NlbGVjdCRpbmNsdWRlXSkKfQpteV9kYXRhX3NlbGVjdF9tIDwtIGZ1bmN0aW9uKHBhdHRlcm5zID0gTlVMTCwgU2V0ID0gTlVMTCl7CiAgYWxsX3NlbGVjdGVkID0gYygpCiAgdHJ5KGlmKGxlbmd0aChwYXR0ZXJucykgPT0gMCkgc3RvcCgiTm8gcGF0dGVybnMgcHJvdmlkZWQiKSkKICBmb3IoaSBpbiBwYXR0ZXJucykKICAgIGFsbF9zZWxlY3RlZCA9IGMoYWxsX3NlbGVjdGVkLCBteV9kYXRhX3NlbGVjdChwYXR0ZXJuID0gaSkpCiAgcmV0dXJuKHVuaXF1ZShhbGxfc2VsZWN0ZWQpKQp9CmBgYAoKIyBBbmFseXNpcwojIyBQbG90dGluZyBmdW5jdGlvbnMKU2V0IHVwIGNvbW1vbiBwYXJhbWV0ZXJzIGZvciB0aHJlc2hvbGRpbmcgYW5kIHBsb3R0aW5nCmBgYHtyfQojIHJlZmVyZW5jZSBQaG80IHBsYXNtaWQgaWRzCnJlZnMgPC0gYygiMTg4IiwgIjE5NCIpCiMgY29sb3JzCmRhdGUuY29sb3JzID0gYyhicmV3ZXIucGFsKG5hbWU9IkRhcmsyIiwgbiA9IDgpLCBicmV3ZXIucGFsKG5hbWU9IlBhaXJlZCIsIG4gPSA4KSkKaG9zdC5jb2xvcnMgPSBjKCJQSE8yIiA9ICJncmF5MzAiLCAicGhvMuKIhiIgPSAiZ3JheTcwIikKcG9pbnQuY29sb3JzID0gYygiUEhPMiIgPSAiZm9yZXN0Z3JlZW4iLCAicGhvMuKIhiIgPSAicHVycGxlNCIpCiMgCmBgYAoKRGF0YSBwcmVwIGFuZCB0cmFuc2Zvcm0KYGBge3J9Cm15X2RhdGFfcHJlcCA8LSBmdW5jdGlvbihzZWxlY3Rpb24pewogICMgZ2l2ZW4gYSBzZWxlY3Rpb24gb2YgY2hpbWVyYSBJRCAocGxhc21pZCksIHByZXBhcmUgYSBkYXRhIGZyYW1lIGZvciBwbG90dGluZwogICMgc3Vic2V0IGRhdGEKICB0bXAgPC0geGltZXJhICU+JSAKICAgIGZpbHRlcihwbGFzbWlkICVpbiUgYyhyZWZzLCBzZWxlY3Rpb24pKSAlPiUgCiAgICBzZWxlY3QocGxhc21pZCwgc3ltYm9sLCBncm91cCkgJT4lIAogICAgaW5uZXJfam9pbihkYXQsIGJ5ID0gInBsYXNtaWQiKSAlPiUgCiAgcmV0dXJuKHRtcCkKfQpgYGAKClBsb3QgUkZQL0dGUCByYXRpbyBhbmQgaW5kaXZpZHVhbCBjb21wb25lbnRzCmBgYHtyfQpteV9wbG90X3JhdGlvIDwtIGZ1bmN0aW9uKHNlbGVjdGlvbil7CiAgdG1wIDwtIG15X2RhdGFfcHJlcChzZWxlY3Rpb24pCiAgcCA8LSB0bXAgJT4lIAogICAgc2VsZWN0KC1jKEZTQy5ILCBuR0ZQLCBuUkZQLCBmbGFnKSkgJT4lIAogICAgbXV0YXRlKGBSL0dgID0gWUwyLkgvQkwxLkgpICU+JSAKICAgIHBpdm90X2xvbmdlcihjb2xzID0gYyhCTDEuSCwgWUwyLkgsIGBSL0dgKSwgCiAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAicGFyYW1ldGVyIiwgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lIAogICAgbXV0YXRlKHBhcmFtZXRlciA9IGZhY3RvcihwYXJhbWV0ZXIsIGxldmVscyA9IGMoIlIvRyIsICJZTDIuSCIsICJCTDEuSCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJSRlAvR0ZQIiwgIlBITzVwUkZQIiwgIlBobzQtR0ZQIikpKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBzeW1ib2wsIHkgPSB2YWx1ZSwgZ3JvdXAgPSBob3N0KSkgKyAKICAgIHN0YXRfc3VtbWFyeShhZXMoZ3JvdXAgPSBob3N0KSwgZnVuLmRhdGEgPSAibWVhbl9jbF9ib290IiwgZ2VvbSA9ICJlcnJvcmJhciIsCiAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjUpLCB3aWR0aCA9IDAuMykgKwogICAgZ2VvbV9iYXIoYWVzKGZpbGwgPSBob3N0KSwgd2lkdGggPSAwLjUsIGFscGhhID0gMC44LAogICAgICAgICAgICAgc3RhdCA9ICJzdW1tYXJ5IiwgZnVuID0gIm1lYW4iLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSkpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGZ1bmN0aW9uKHgpIHN1YnNldCh4LCAhc3ltYm9sICVpbiUgYygiQ0NDQ0MiLCAiU1NTU1MiKSksCiAgICAgICAgICAgICAgIGFlcyhncm91cCA9IGhvc3QsIGNvbG9yID0gaG9zdCksIHNpemUgPSAxLCBzaGFwZSA9IDMsIGFscGhhID0gMC45LAogICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcmRvZGdlKGRvZGdlLndpZHRoID0gMC41LCBqaXR0ZXIud2lkdGggPSAwLjEpKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcG9pbnQuY29sb3JzKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBob3N0LmNvbG9ycykgKwogICAgZmFjZXRfZ3JpZChwYXJhbWV0ZXJ+Z3JvdXAsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZV94IikgKwogICAgdGhlbWVfYncoYmFzZV9zaXplID0gMTgpICsgYmFja2dyb3VuZF9ncmlkKG1pbm9yID0gIm5vbmUiKSArIAogICAgeGxhYigiUGhvNCBjaGltZXJhIikgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMCwgaGp1c3QgPSAxLCBmYW1pbHkgPSAibW9ubyIpLAogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQogIHJldHVybihwKSAKfQpgYGAKClBsb3QgY2hpbWVyYSByZWxhdGl2ZSBhY3Rpdml0eSB0byBTY1BobzQgdy8gUGhvMiBhbmQgYm9vc3QgZmFjdG9yCmBgYHtyfQpteV9wbG90X3JlbF9hY3QgPC0gZnVuY3Rpb24oc2VsZWN0aW9uKXsKICAjIGdpdmVuIGEgc2VsZWN0aW9uIG9mIGNoaW1lcmEgSURzLCBwbG90IHRoZWlyIGZ1bmN0aW9uYWxpdHkgdy9QSE8yCiAgIyByZWxhdGl2ZSB0byBTY1BobzQsIGFuZCB0aGVpciBib29zdAogIHAgPC0gZmlsdGVyKHhpbWVyYSwgcGxhc21pZCAlaW4lIGMocmVmcywgc2VsZWN0aW9uKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBzeW1ib2wsIHkgPSBzX1BITzIpKSArCiAgICBnZW9tX2NvbCh3aWR0aCA9IDAuMywgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImdyYXk4MCIpICsgCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZSA9IDIsIGNvbG9yID0gImdyYXkzMCIpICsKICAgIGZhY2V0X2dyaWQoLn5ncm91cCwgc2NhbGVzID0gImZyZWVfeCIsIHNwYWNlID0gImZyZWVfeCIpICsgCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArCiAgICB4bGFiKCJQaG80IGNoaW1lcmEiKSArIHlsYWIoIkE8c3ViPlBITzI8L3N1Yj4sIGNoaW1lcmEvU2NQaG80IikgKwogICAgZ2d0aXRsZSgiQ2hpbWVyYSBmdW5jdGlvbiBpbiA8ZW0+Uy4gY2VyZXZpc2lhZTwvZW0+IikgKwogICAgdGhlbWVfYncoYmFzZV9zaXplID0gMTgpICsgYmFja2dyb3VuZF9ncmlkKG1pbm9yID0gIm5vbmUiKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDMwLCBoanVzdCA9IDEsIGZhbWlseSA9ICJtb25vIiksCiAgICAgICAgICAjc3RyaXAudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9tYXJrZG93bihoanVzdCA9IDAuNSksCiAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X21hcmtkb3duKCksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIikKICByZXR1cm4ocCkKfQpgYGAKCmBgYHtyfQpteV9wbG90X2Jvb3N0IDwtIGZ1bmN0aW9uKHNlbGVjdGlvbil7CiAgIyBnaXZlbiBhIHNlbGVjdGlvbiBvZiBjaGltZXJhIElEcywgcGxvdCB0aGVpciBmdW5jdGlvbmFsaXR5IHcvUEhPMgogICMgcmVsYXRpdmUgdG8gU2NQaG80LCBhbmQgdGhlaXIgYm9vc3QKICAjIGRhdAogIHRtcCA8LSBmaWx0ZXIoeGltZXJhLCBwbGFzbWlkICVpbiUgYyhyZWZzLCBzZWxlY3Rpb24pKSAlPiUgCiAgICBtdXRhdGUocGVyY19waG8yID0gQV9waG8yL0FfUEhPMikgJT4lIAogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKHNfUEhPMiwgYm9vc3QsIHBlcmNfcGhvMiksIAogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInBhcmFtZXRlciIsIHZhbHVlc190byA9ICJyYXRpbyIpCiAgIyBsYWJlbGxlcgogIHBhci5leHBsYWluIDwtIGMoCiAgICBzX1BITzIgPSAiUmVsLiBBPHN1Yj5QSE8yPC9zdWI+IiwKICAgIGJvb3N0ID0gIkJvb3N0IiwKICAgIHBlcmNfcGhvMiA9ICIlQTxzdWI+cGhvMuKIhjwvc3ViPiIKICApCiAgcCA8LSBnZ3Bsb3QodG1wLCBhZXMoeCA9IHN5bWJvbCwgeSA9IHJhdGlvKSkgKwogICAgZ2VvbV9jb2wod2lkdGggPSAwLjMsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJncmF5ODAiKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZSA9IDIsIGNvbG9yID0gImdyYXkzMCIpICsKICAgIGZhY2V0X2dyaWQocGFyYW1ldGVyfmdyb3VwLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWVfeCIsCiAgICAgICAgICAgICAgbGFiZWxsZXIgPSBsYWJlbGxlcihwYXJhbWV0ZXIgPSBwYXIuZXhwbGFpbikpICsKICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE4KSArCiAgICBiYWNrZ3JvdW5kX2dyaWQobWlub3IgPSAibm9uZSIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMzAsIGhqdXN0ID0gMSwgZmFtaWx5ID0gIm1vbm8iKSwKICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfbWFya2Rvd24oc2l6ZSA9IHJlbCgwLjkpKQogICAgICAgICAgI3Bsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpCiAgICApCiAgcmV0dXJuKHApCn0KYGBgCgojIyBIaWdoIHZhcmlhbmNlIHNhbXBsZXMKClN1bW1hcml6ZSB0aGUgYmFja2dyb3VuZCBzdWJ0cmFjdGVkIGRhdGEgYnkgY2FsY3VsYXRpbmcgdGhlIG1lYW5zIGFuZCBjdiBmb3IgZWFjaCBzdHJhaW4uCmBgYHtyfQpjdiA8LSBkYXQgJT4lIAogIHNlbGVjdCgtbkdGUCwgLW5SRlApICU+JQogIHBpdm90X2xvbmdlcihGU0MuSDpZTDIuSCwgbmFtZXNfdG8gPSAicGFyYW1ldGVyIiwgdmFsdWVzX3RvID0gImludGVuc2l0eSIpICU+JSAKICBncm91cF9ieShkYXRlLCBwbGFzbWlkLCBob3N0LCBwYXJhbWV0ZXIpICU+JSAKICBzdW1tYXJpemUoCiAgICBuID0gbigpLAogICAgbWVhbiA9IG1lYW4oaW50ZW5zaXR5KSwKICAgIGN2ID0gc2QoaW50ZW5zaXR5KS9tZWFuKGludGVuc2l0eSksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKSAlPiUgCiAgYXJyYW5nZShkZXNjKGN2KSkKYGBgCgpOdW1iZXIgb2YgcmVwbGljYXRlcyBsZWZ0IGZvciBlYWNoIHNhbXBsZQpgYGB7cn0KZXhwdCA8LSBkYXQgJT4lIAogIGZpbHRlcihob3N0ICVpbiUgYygiUEhPMiIsICJwaG8y4oiGIiksICFwbGFzbWlkICVpbiUgYygiMTg4IiwgIjE5NCIpKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgcGxhc21pZCwgaG9zdCkgJT4lIAogIHN1bW1hcml6ZShuID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKQoKZXhwdCAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gcGxhc21pZCwgeSA9IG4pKSArCiAgZ2VvbV9jb2woYWVzKGZpbGwgPSBob3N0KSkgKyAKICBmYWNldF9ncmlkKGRhdGUgfiAuKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiUEhPMiIgPSAiZ3JheTMwIiwgInBobzLiiIYiID0gImdyYXk3MCIpKSArCiAgdGhlbWVfbWluaW1hbCgpICsgYmFja2dyb3VuZF9ncmlkKG1ham9yID0gIm5vbmUiKSArIHBhbmVsX2JvcmRlcihzaXplID0gMC41KSArCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWUgPSAiUmVwbGljYXRlcyIsIGJyZWFrcyA9IGMoNikpICsgeGxhYihOVUxMKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksCiAgICAgICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYApVc2UgdGhlIGNvbnRyb2wgc3RyYWluIChwSDE5NCB3aXRoIFBITzIpIHRvIGlkZW50aWZ5IGFuZCBjb3JyZWN0IGZvciBzeXN0ZW1hdGljIGJpYXNlcwpgYGB7cn0KY29udHJvbCA8LSBmaWx0ZXIoZGF0LCBwbGFzbWlkID09ICIxOTQiLCBob3N0ID09ICJQSE8yIikgJT4lIAogIHNlcGFyYXRlKHdlbGwsIGludG8gPSBjKCJyb3ciLCAiY29sIiksIHNlcCA9IDEpICU+JSAKICBkcm9wbGV2ZWxzKCkKYGBgCgpNb2RlbCBmb3IgbU5lb24KYGBge3J9CmdmcC5tb2RlbC4wIDwtIGxtKEJMMS5IIH4gbG9nMTAoZXZlbnRzKSArIGRhdGUgKyByb3cqY29sLCBkYXRhID0gY29udHJvbCkKc3RlcChnZnAubW9kZWwuMCkKZ2ZwLm1vZGVsLjEgPC0gbG0oQkwxLkggfiBkYXRlICsgY29sLCBkYXRhID0gY29udHJvbCkKYGBgCgpNb2RlbCBmb3IgUEhPNXByOjpSRlAKYGBge3J9CnJmcC5tb2RlbC4wIDwtIGxtKFlMMi5IIH4gbG9nMTAoZXZlbnRzKSArIGRhdGUgKyByb3cqY29sLCBkYXRhID0gY29udHJvbCkKc3RlcChyZnAubW9kZWwuMCkKcmZwLm1vZGVsLjEgPC0gbG0oWUwyLkggfiBsb2cxMChldmVudHMpICsgZGF0ZSArIHJvdyArIGNvbCwgZGF0YSA9IGNvbnRyb2wpCmBgYAo+IHRoZXJlIGFyZSBtb3JlIHN5c3RlbWF0aWMgc2hpZnRzIGluIHRoZSBSRlAsIHNpZ25pZmljYW50IGZvciByb3csIGNvbCwgZGF0ZSBhbmQgYWxzbyAjIG9mIGV2ZW50cwo+IGhvd2V2ZXIsIEkgd29uJ3QgYmUgcmVtb3ZpbmcgdGhlc2UgZWZmZWN0cyB5ZXQsIGJlY2F1c2UgSSd2ZSBmb3VuZCB0aGF0IFJGUC9HRlAgcmF0aW9zIGFyZSBwcmV0dHkgY29uc2lzdGVudCBhY3Jvc3MgZGF5cy4gSW4gb3RoZXIgd29yZHMsIHRoZSB2YXJpYXRpb24gaW4gR0ZQIGFuZCBSRlAgbWF5IGJlIGNhbmNlbGxlZCBvdXQuCgpDaGVjayBmb3IgZWFjaCBwbGFzbWlkIGhvdyBjb25zaXN0ZW50IGFyZSB0aGUgbWVhc3VyZW1lbnRzIGJldHdlZW4gZGF5cwpgYGB7cn0KdG1wIDwtIGRhdCAlPiUgCiAgIyByZW1vdmUgb25lIHNhbXBsZSB3aXRoIG9ubHkgb25lIHZhbGlkIGRheSBvZiBleHBlcmltZW50CiAgZmlsdGVyKCEocGxhc21pZCA9PSAiMjE4IiAmIGhvc3QgPT0gIlBITzIiKSwgIXBsYXNtaWQgJWluJSBjKCIxODgiLCAiMTk0IiwgTkEpKSAlPiUgCiAgbmVzdChkYXRhID0gYyhkYXRlLCBCTDEuSCwgWUwyLkgpLCAuYnkgPSBjKHBsYXNtaWQsIGhvc3QpKQoKZGF5LnZhci5nZnAgPC0gdG1wICU+JSAKICBtdXRhdGUobW9kZWwgPSBtYXAoZGF0YSwgZnVuY3Rpb24oZGYpIGxtKEJMMS5IIH4gZGF0ZSwgZGF0YSA9IGRmKSksCiAgICAgICAgIHRpZGllZCA9IG1hcChtb2RlbCwgYnJvb206OnRpZHkpKSAlPiUgCiAgdW5uZXN0KHRpZGllZCkgJT4lIAogIGZpbHRlcih0ZXJtICE9ICIoSW50ZXJjZXB0KSIpICU+JSAKICBtdXRhdGUocC5hZGogPSBwLmFkanVzdChwLnZhbHVlLCBtZXRob2QgPSAiQkgiKSkgJT4lIAogIHNlbGVjdCgtZGF0YSwgLW1vZGVsKSAlPiUgCiAgZmlsdGVyKHAuYWRqIDwgMC4xMCkgJT4lIAogIGFycmFuZ2UocGxhc21pZCwgaG9zdCkKCmRheS52YXIucmZwIDwtIHRtcCAlPiUgCiAgbXV0YXRlKG1vZGVsID0gbWFwKGRhdGEsIGZ1bmN0aW9uKGRmKSBsbShZTDIuSCB+IGRhdGUsIGRhdGEgPSBkZikpLAogICAgICAgICB0aWRpZWQgPSBtYXAobW9kZWwsIGJyb29tOjp0aWR5KSkgJT4lIAogIHVubmVzdCh0aWRpZWQpICU+JSAKICBmaWx0ZXIodGVybSAhPSAiKEludGVyY2VwdCkiKSAlPiUgCiAgbXV0YXRlKHAuYWRqID0gcC5hZGp1c3QocC52YWx1ZSwgbWV0aG9kID0gIkJIIikpICU+JSAKICBzZWxlY3QoLWRhdGEsIC1tb2RlbCkgJT4lIAogIGZpbHRlcihwLmFkaiA8IDAuMTApICU+JSAKICBhcnJhbmdlKHBsYXNtaWQsIGhvc3QpCmBgYAoKYGBge3J9CiMgZXh0cmFjdCB4aW1lcmEgbmFtZXMKcmVmcyA8LSBjKCIxODgiLCIxOTQiKQojIG1ha2UgYSB0ZXN0IHNldApkYXkudmFyLmdmcC5saXN0IDwtIHVuaXF1ZShkYXkudmFyLmdmcCRwbGFzbWlkKQpkYXkudmFyLnJmcC5saXN0IDwtIHVuaXF1ZShkYXkudmFyLnJmcCRwbGFzbWlkKQpgYGAKCkhpZ2ggZGF5LXRvLWRheSBHRlAgdmFyaWFuY2U6IGByIGRheS52YXIuZ2ZwLmxpc3RgCkhpZ2ggZGF5LXRvLWRheSBSRlAgdmFyaWFuY2U6IGByIGRheS52YXIucmZwLmxpc3RgCgpQbG90dGluZyBjb21wb25lbnRzIGZvciBjaGltZXJhcyB3aXRoIGhpZ2ggZGF5LXRvLWRheSB2YXJpYW5jZSBpbiBQaG80LW1OZW9uCmBgYHtyfQpwIDwtIG15X3Bsb3RfcmF0aW8oYyhyZWZzLGRheS52YXIuZ2ZwLmxpc3QpKSArIAogICAgZ2VvbV9wb2ludChkYXRhID0gZnVuY3Rpb24oeCkgc3Vic2V0KHgsICFzeW1ib2wgJWluJSBjKCJDQ0NDQyIsICJTU1NTUyIpKSwKICAgICAgICAgICAgICAgYWVzKGdyb3VwID0gaG9zdCwgY29sb3IgPSBkYXRlKSwgc2l6ZSA9IDEsIHNoYXBlID0gMywgYWxwaGEgPSAwLjksCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyZG9kZ2UoZG9kZ2Uud2lkdGggPSAwLjUsIGppdHRlci53aWR0aCA9IDAuMSkpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBkYXRlLmNvbG9ycywgZ3VpZGUgPSAibm9uZSIpCnAKYGBgCj4gV2F0Y2ggb3V0IGZvciBDU0NzY0MsIFNDQ3NTLCBTQ0NzUwoKUGxvdHRpbmcgY29tcG9uZW50cyBmb3IgY2hpbWVyYXMgd2l0aCBoaWdoIGRheS10by1kYXkgdmFyaWFuY2UgaW4gX1BITzVwcl8tbUNoZXJyeQpgYGB7cn0KcCA8LSBteV9wbG90X3JhdGlvKGMocmVmcyxkYXkudmFyLnJmcC5saXN0KSkgKyAKICAgIGdlb21fcG9pbnQoZGF0YSA9IGZ1bmN0aW9uKHgpIHN1YnNldCh4LCAhc3ltYm9sICVpbiUgYygiQ0NDQ0MiLCAiU1NTU1MiKSksCiAgICAgICAgICAgICAgIGFlcyhncm91cCA9IGhvc3QsIGNvbG9yID0gZGF0ZSksIHNpemUgPSAxLCBzaGFwZSA9IDMsIGFscGhhID0gMC45LAogICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC41KSkgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGRhdGUuY29sb3JzLCBndWlkZSA9ICJub25lIikKcApgYGAKPiBtb3N0IG9mIHRoZSBkYXktdG8tZGF5IHZhcmlhbmNlIGFyZSBjYW5jZWxlZCBvdXQgYWZ0ZXIgUkZQL0dGUCBub3JtYWxpemF0aW9uCgojIyBBbGwgY2hpbWVyYSwgc2NhdHRlciBwbG90CkRlc2lnbiB0aGUgcGxvdAoKYGBge3J9Cm15X3NjYXR0ZXJfcGxvdCA8LSBmdW5jdGlvbihwYXR0ZXJuKXsKICBzZWxlY3Rpb24gPSBteV9kYXRhX3NlbGVjdChwYXR0ZXJuID0gcGF0dGVybikKICBzY2F0dGVyLmNvbG9ycyA9IGMoIlNjUGhvNCIgPSAiZm9yZXN0Z3JlZW4iLCAiQ2dQaG80IiA9ICJibHVlMyIsICJjeWFuMiIsICJvdGhlciIgPSAiZ3JheTIwIikKICBuYW1lcyhzY2F0dGVyLmNvbG9ycylbM10gPSBwYXR0ZXJuCiAgcCA8LSB4aW1lcmEgJT4lIAogICAgbXV0YXRlKEFfUEhPMiA9IHNpZ25pZihBX1BITzIsIGRpZ2l0cyA9IDIpLAogICAgICAgICAgIEFfcGhvMiA9IHNpZ25pZihBX3BobzIsIGRpZ2l0cyA9IDIpLAogICAgICAgICAgIGdyb3VwID0gY2FzZV93aGVuKAogICAgICAgICAgICAgc3ltYm9sID09ICJDQ0NDQyIgfiAiQ2dQaG80IiwKICAgICAgICAgICAgIHN5bWJvbCA9PSAiU1NTU1MiIH4gIlNjUGhvNCIsCiAgICAgICAgICAgICBwbGFzbWlkICVpbiUgc2VsZWN0aW9uIH4gcGF0dGVybiwKICAgICAgICAgICAgIC5kZWZhdWx0ID0gIm90aGVyIgogICAgICAgICAgICksCiAgICAgICAgICAgZ3JvdXAgPSBmY3RfcmVsZXZlbChncm91cCwgbmFtZXMoc2NhdHRlci5jb2xvcnMpKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gQV9QSE8yLCB5ID0gQV9waG8yLCBsYWJlbCA9IHN5bWJvbCkpICsgCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGdyb3VwKSwgc2l6ZSA9IDIuNSkgKyAKICAgIHNjYWxlX2NvbG9yX21hbnVhbCgiUGhvNCB0eXBlIiwgdmFsdWVzID0gc2NhdHRlci5jb2xvcnMpICsKICAgIGdlb21fYWJsaW5lKHNsb3BlID0gMSkgKwogICAgdGhlbWVfZ3JheShiYXNlX3NpemUgPSAxNCkKICByZXR1cm4ocCkKfQpgYGAKCmBgYHtyfQpteV9zY2F0dGVyX3Bsb3RfZml4IDwtIGZ1bmN0aW9uKCl7CiAgIyB0aGlzIGZ1bmN0aW9uIGlzIHRoZSBzYW1lIGFzIGFib3ZlLCBidXQgaXMgdXNlZCB0byBwbG90IHJlZ2lvbiA0CiAgIyBlZmZlY3RzIGFsb25lLCBhbmQgZG9lc24ndCB0YWtlIGFueSBpbnB1dAogIHMxID0gbXlfZGF0YV9zZWxlY3QocGF0dGVybiA9ICJYWFhDQ1giKQogIHMyID0gbXlfZGF0YV9zZWxlY3QocGF0dGVybiA9ICJYWFhTU1giKQogIHNjYXR0ZXIuY29sb3JzID0gYygiU2NQaG80IiA9ICJmb3Jlc3RncmVlbiIsICJDZ1BobzQiID0gImJsdWUzIiwgCiAgICAgICAgICAgICAgICAgICAgICJQMklEOkNnIiA9ICJjeWFuMiIsICJQMklEOlNjIiA9ICJwYWxlZ3JlZW4iLAogICAgICAgICAgICAgICAgICAgICAiUDJJRDptaXhlZCIgPSAiZ3JheTIwIikKICBwIDwtIHhpbWVyYSAlPiUgCiAgICBtdXRhdGUoQV9QSE8yID0gc2lnbmlmKEFfUEhPMiwgZGlnaXRzID0gMiksCiAgICAgICAgICAgQV9waG8yID0gc2lnbmlmKEFfcGhvMiwgZGlnaXRzID0gMiksCiAgICAgICAgICAgZ3JvdXAgPSBjYXNlX3doZW4oCiAgICAgICAgICAgICBzeW1ib2wgPT0gIkNDQ0NDIiB+ICJDZ1BobzQiLAogICAgICAgICAgICAgc3ltYm9sID09ICJTU1NTUyIgfiAiU2NQaG80IiwKICAgICAgICAgICAgIHBsYXNtaWQgJWluJSBzMSB+ICJQMklEOkNnIiwKICAgICAgICAgICAgIHBsYXNtaWQgJWluJSBzMiB+ICJQMklEOlNjIiwKICAgICAgICAgICAgIC5kZWZhdWx0ID0gIlAySUQ6bWl4ZWQiCiAgICAgICAgICAgKSwKICAgICAgICAgICBncm91cCA9IGZjdF9yZWxldmVsKGdyb3VwLCBuYW1lcyhzY2F0dGVyLmNvbG9ycykpKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBBX1BITzIsIHkgPSBBX3BobzIsIGxhYmVsID0gc3ltYm9sKSkgKyAKICAgIGdlb21fYWJsaW5lKHNsb3BlID0gMSkgKwogICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBncm91cCksIHNpemUgPSAyLjUpICsgCiAgICBzY2FsZV9jb2xvcl9tYW51YWwoTlVMTCwgdmFsdWVzID0gc2NhdHRlci5jb2xvcnMpICsKICAgIGxhYnMoeCA9IGJxdW90ZShBW1BITzJdKSwgeSA9IGJxdW90ZShBW3BobzJdKSkgKwogICAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxNikgKwogICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpCiAgcmV0dXJuKHApCn0KYGBgCgpgYGB7cn0KcCA8LSBteV9zY2F0dGVyX3Bsb3RfZml4KCkKZ2dzYXZlKGZpbGVuYW1lID0gIi4uL2ltZy8yMDIzMTIyMC1hbGwtY2hpbWVyYS1zY2F0dGVyLWNvbG9yLWJ5LVAySUQucG5nIiwKICAgICAgIHBsb3QgPSBwLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQsIGRwaSA9IDMwMCkKZ2dwbG90bHkocCArIGxhYnMoeCA9ICJBPHN1Yj5QSE8yPC9zdWI+IiwgeSA9ICJBPHN1Yj5waG8yPC9zdWI+IikgKwogICAgICAgICAgIHRoZW1lX2dyYXkoYmFzZV9zaXplID0gMTYpICsKICAgICAgICAgICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfbWFya2Rvd24oKSksIAogICAgICAgICB0b29sdGlwID0gYygibGFiZWwiLCAieCIsICJ5IikpCmBgYAoKIyMgQU5PVkEKYGBge3J9CnNwbGl0IDwtIGMoMSwxLDEsMSwxKTsgbmFtZXMoc3BsaXQpIDwtIHBhc3RlMCgiUCIsIDE6NSkKdG1wIDwtIHhpbWVyYSAlPiUgCiAgZmlsdGVyKHNldCA9PSAiTSIsIGdyb3VwICE9ICJuLmYuIikgJT4lIAogIHNlcGFyYXRlX3dpZGVyX3Bvc2l0aW9uKHN5bWJvbCwgc3BsaXQpICU+JSAKICBtdXRhdGUoYWNyb3NzKFAxOlA1LCB+ZmFjdG9yKC54LCBsZXZlbHMgPSBjKCJTIiwgIkMiKSkpKQpsbSA8LSBsbShBX3BobzIgfiAoUDErUDIrUDMrUDQrUDUpLCBkYXRhID0gdG1wKQpzdW1tYXJ5KGxtKQpgYGAKVGhlIG1haW4gZWZmZWN0cyB3ZXJlIGNhbGN1bGF0ZWQgYnkgYXZlcmFnaW5nIG92ZXIgYWxsIGNoaW1lcmFzIHdpdGggQ2dQaG80IHJlZ2lvbiBhdCB0aGUgcmVzcGVjdGl2ZSBwb3NpdGlvbi4gSSdkIGxpa2UgdG8gYnJlYWsgdGhlbSBkb3duIGJ5IGJhY2tncm91bmRzLiBGb3IgZXhhbXBsZSwgZm9yIHJlZ2lvbiAzLCBJJ2QgbGlrZSB0byBzZWUgdGhlIHBhaXJ3aXNlIGNvbXBhcmlzb25zIGJldHdlZW4gQ0NDU1MgYW5kIENDU1NTLCB3aGVyZSBvbmx5IHJlZ2lvbiAzIGRpZmZlcnMuIFRoZSBzdGVwcyBhcmUKCjEuIHNlbGVjdCB0aGUgcmVnaW9uIHRvIGJlIGNvbXBhcmVkLiBzcGxpdCB0aGUgc3ltYm9sIGludG8gdHdvIHBhcnRzIC0gdGhlIGdlbm90eXBlIG9mIHRoZSBmb2NhbCByZWdpb24gYW5kIHRoZSByZXN0CjIuIGdyb3VwIGJ5IHRoZSBzZWNvbmQgcGFydCAocmVzdCkgYW5kIGNhbGN1bGF0ZSB0aGUgZGlmZmVyZW50aWFsCgpgYGB7cn0KbXlfY2FsY19yZWdpb25fZWZmZWN0IDwtIGZ1bmN0aW9uKHJlZ2lvbiwgdmFyaWFibGUpewogICMgdGhpcyBmdW5jdGlvbiB0YWtlcyB0aGUgbmFtZSBvZiBhIHZhcmlhYmxlIG9mIGludGVyZXN0CiAgIyB4IHNwZWNpZmllcyB0aGUgZm9yZWdyb3VuZCByZWdpb24sIHdoaWNoIHdpbGwgYmUgZXhhbWluZWQgZm9yIGl0cyBlZmZlY3Qgb24KICAjIHRoZSB2YXJpYWJsZSBvZiBpbnRlcmVzdC4KICAjIGl0IHRoZW4gdHJhbnNmb3JtcyB0aGUgeGltZXJhIGRhdGEgZnJhbWUgdG8gcHJlc2VydmUgb25seSB0aGUgdmFyaWFibGUgb2YKICAjIGludGVyZXN0LCBwaXZvdHMgaXQgd2lkZXIgYWZ0ZXIgZ3JvdXBpbmcgYnkgdGhlIGJhY2tncm91bmQgY29tcG9zaXRpb24uCiAgCiAgIyBwcmVwYXJlIHRoZSBkYXRhIGJ5IG11dGF0aW5nIHRoZSBzeW1ib2wgY29sdW1uIGludG8gZmcgYW5kIGJnCiAgdmFsaWQudmFyIDwtIGMoIkFfUEhPMiIsICJBX3BobzIiLCAic19QSE8yIiwgInNwaG8yIiwgImJvb3N0IikKICBpZighdmFyaWFibGUgJWluJSB2YWxpZC52YXIpCiAgICBzdG9wKHBhc3RlMCgiUGxlYXNlIHNwZWNpZnkgb25lIG9mIHRoZSB2YWxpZCB2YXJpYWJsZSBuYW1lczoiLCAKICAgICAgICAgICAgICAgIHBhc3RlKHZhbGlkLnZhciwgY29sbGFwc2UgPSAiLCAiKSkpCiAgdG1wIDwtIHhpbWVyYSAlPiUgCiAgICBmaWx0ZXIoc2V0ID09ICJNIikgJT4lIAogICAgc2VsZWN0KHBsYXNtaWQsIHN5bWJvbCwgdmFyID0ge3sgdmFyaWFibGUgfX0pICU+JSAKICAgIG11dGF0ZShmZyA9IHN0cl9zdWIoc3ltYm9sLCByZWdpb24sIHJlZ2lvbikgJT4lIHRvdXBwZXIoKSwKICAgICAgICAgICBiZyA9IHN5bWJvbCAlPiUgdG91cHBlcigpKQogICMgcmVwbGFjZSB0aGUgZm9yZWdyb3VuZCByZWdpb24gd2l0aCBYIGZvciBncm91cGluZwogIHN0cl9zdWIodG1wJGJnLCByZWdpb24sIHJlZ2lvbikgPC0gIlgiCiAgIyByZW9yZ2FuaXplIHRoZSB0aWJibGUgZm9yIGVhc2llciBoYW5kbGluZywgb3B0aW9uYWwKICB0bXAgPC0gcmVsb2NhdGUodG1wLCBmZywgYmcsIC5iZWZvcmUgPSBzeW1ib2wpICU+JSBzZWxlY3QoLXN5bWJvbCkKICAjIHBpdm90IHRoZSBkYXRhIGludG8gYSB3aWRlIGZvcm1hdCBzdWNoIHRoYXQgZm9yIGVhY2ggYmFja2dyb3VuZCwgdGhlcmUKICAjIGFyZSB0d28gdmFsdWVzIGZvciB0aGUgdmFyaWFibGUgb2YgaW50ZXJlc3QsIG9uZSBmcm9tIHRoZSBjaGltZXJhIHdpdGggCiAgIyBDZ1BobzQncyB2ZXJzaW9uIGluIHRoZSBmb3JlZ3JvdW5kIGFuZCBhbm90aGVyIHdpdGggU2NQaG80J3MgdmVyc2lvbgogIHRtcCA8LSB0bXAgJT4lIAogICAgc2VsZWN0KHBsYXNtaWQsIGZnLCBiZywgdmFyKSAlPiUgCiAgICBwaXZvdF93aWRlcihpZF9jb2xzID0gYmcsIG5hbWVzX2Zyb20gPSAiZmciLCAKICAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gYyhwbGFzbWlkLCB2YXIpKSAlPiUgCiAgICB1bml0ZShwbGFzbWlkLCBzdGFydHNfd2l0aCgicGxhc21pZCIpKSAlPiUKICAgIG11dGF0ZShsYWJlbCA9IHBhc3RlKGJnLCBwbGFzbWlkLCBzZXAgPSAiXG4iKSkKICByZXR1cm4odG1wKQp9CgpteV9wbG90X3JlZ2lvbl9lZmZlY3Rfb25ldmFyIDwtIGZ1bmN0aW9uKHJlZ2lvbiwgdmFyaWFibGUpewogICMgdGhpcyBmdW5jdGlvbiB1c2VzIGBteV9jYWxjX3JlZ2lvbl9lZmZlY3RgIG91dHB1dCBhcyB0aGUgZGF0YQogICMgYW5kIG1ha2VzIGEgeHkgc2NhdHRlciBwbG90LCB3aGVyZSB4IHNob3dzIHRoZSB2YWx1ZSBvZiB0aGUgdmFyaWFibGUgb2YgCiAgIyBpbnRlcmVzdCB3aXRoIENnUGhvNCBpbiB0aGUgZm9jYWwgcmVnaW9uLCBhbmQgeSBmb3IgdGhlIFNjUGhvNCB2ZXJzaW9uCiAgdG1wIDwtIG15X2NhbGNfcmVnaW9uX2VmZmVjdChyZWdpb24sIHZhcmlhYmxlKQogIHAgPC0gZ2dwbG90KHRtcCwgYWVzKHggPSB2YXJfQywgeSA9IHZhcl9TLCBsYWJlbCA9IGxhYmVsKSkgKwogICAgZ2VvbV9wb2ludChzaXplID0gMi41KSArIAogICAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxKSArCiAgICB4bGFiKHBhc3RlMCgiUmVnaW9uICIsIHJlZ2lvbiwgIiBmcm9tIENnUGhvNCIpKSArCiAgICB5bGFiKHBhc3RlMCgiUmVnaW9uICIsIHJlZ2lvbiwgIiBmcm9tIFNjUGhvNCIpKSArCiAgICB4bGltKDAsIE5BKSArIHlsaW0oMCwgTkEpICsKICAgIGdndGl0bGUocGFzdGUwKCJFZmZlY3Qgb24gIiwgdmFyaWFibGUpKSArCiAgICB0aGVtZV9ncmF5KGJhc2Vfc2l6ZSA9IDE2KSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKICByZXR1cm4ocCkKfQpgYGAKCmBgYAp4ID0gNQpwMSA8LSBteV9wbG90X3JlZ2lvbl9lZmZlY3Rfb25ldmFyKHgsICJBX1BITzIiKQpwMiA8LSBteV9wbG90X3JlZ2lvbl9lZmZlY3Rfb25ldmFyKHgsICJBX3BobzIiKQpzdWJwbG90KHAxLCBwMiwgbWFyZ2luID0gMC4wNSkgJT4lIAogIGxheW91dCh0aXRsZSA9IHBhc3RlKCJSZWdpb24iLCB4LCAic3dhcCBlZmZlY3Qgb24gQV9QSE8yIGFuZCBBX3BobzIiLCBzZXAgPSAiICIpLAogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSBwYXN0ZTAoIlJlZ2lvbiAiLCB4LCAiIGZyb20gQ2dQaG80IikpLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSBwYXN0ZTAoIlJlZ2lvbiAiLCB4LCAiIGZyb20gU2NQaG80IikpICkKYGBgCgpIZXJlLCBJJ2QgbGlrZSB0byB0YWtlIHdoYXQgSSBidWlsZCBhYm92ZSBhbmQgY3JlYXRlIGEgbmV3IHRpYmJsZSwgaW4gd2hpY2ggZWFjaCByb3cgaXMgYSBkaWZmZXJlbnQgYmFja2dyb3VuZCAobWFrZXVwIG9mIHRoZSBjaGltZXJhIGV4Y2VwdCBmb3IgdGhlIGZvY2FsIHJlZ2lvbikuIFRoZSB2YWx1ZSBjb2x1bW5zIGFyZToKCjEuIGRBX1BITzIgPSBBX1BITzJfQ2cgLSBBX1BITzJfU2MKMi4gZEFfcGhvMiA9IEFfcGhvMl9DZyAtIEFfcGhvMl9TYwozLiBBX1BITzJfU2MgPSBBX1BITzJfU2MKClRoZSBnb2FsIGlzIHRvIHBsb3QgZEFfUEhPMiBhbmQgZEFfcGhvMiBzaWRlLWJ5LXNpZGUgZm9yIGVhY2ggYmFja2dyb3VuZC4KYGBge3J9Cm15X2NvbXBfcmVnaW9uX2VmZmVjdCA8LSBmdW5jdGlvbihyZWdpb24pewogICMgdGhpcyBmdW5jdGlvbiB1c2VzIG15X2NhbGNfcmVnaW9uX2VmZmVjdCB0byBnZXQgdGhlIHZhbHVlIGZvciB0aGUgdmFyaWFibGUgb2YgaW50ZXJlc3QKICAjIHdpdGggZWl0aGVyIENnIG9yIFNjIHZlcnNpb24gaW4gdGhlIGZvY2FsIHJlZ2lvbiwgc2VwYXJhdGVseSBmb3IgZWFjaCBiYWNrZ3JvdW5kIGNvbXBvc2l0aW9uCiAgIyBpdCBkb2VzIHNvIGZvciB0d28gdmFyaWFibGVzLCBBX1BITzIgYW5kIEFfcGhvMiwgdGhlbiBjYWxjdWxhdGUgZEFfUEhPMiwgZEFfcGhvMiwgYW5kCiAgIyBjb21iaW5lIHRoZW0KICBQSE8yID0gbXlfY2FsY19yZWdpb25fZWZmZWN0KHJlZ2lvbiwgIkFfUEhPMiIpICU+JSAKICAgIG11dGF0ZShkQV9QSE8yID0gdmFyX0MgLSB2YXJfUywKICAgICAgICAgICAjIG1lYW4gQV9QSE8yCiAgICAgICAgICAgTV9QSE8yID0gKHZhcl9TICsgdmFyX0MpLzIsCiAgICAgICAgICAgTkYgPSBpZmVsc2UoTV9QSE8yIDw9My41LCBUUlVFLCBGQUxTRSkpICU+JSAKICAgIHNlbGVjdCgtdmFyX1MsIC12YXJfQykKICAKICBwaG8yID0gbXlfY2FsY19yZWdpb25fZWZmZWN0KHJlZ2lvbiwgIkFfcGhvMiIpICU+JSAKICAgIG11dGF0ZShkQV9waG8yID0gdmFyX0MgLSB2YXJfUywgCiAgICAgICAgICAgTV9waG8yID0gKHZhcl9TICsgdmFyX0MpLzIpICU+JSAKICAgIHNlbGVjdCgtdmFyX1MsIC12YXJfQykKICAKICBkYXQgPC0gZnVsbF9qb2luKFBITzIsIHBobzIsIGJ5ID0gYygiYmciLCAicGxhc21pZCIsICJsYWJlbCIpKSAlPiUgCiAgICBzZWxlY3QoYmcsIHBsYXNtaWQsIGRBX1BITzIsIGRBX3BobzIsIE1fUEhPMiwgTV9waG8yLCBORikKICAKICByZXR1cm4oZGF0KQp9CgpteV9wbG90X3JlZ2lvbl9lZmZlY3RfdHdvdmFyX2xpbmUgPC0gZnVuY3Rpb24ocmVnaW9uLCBoaWdobGlnaHQgPSAibm9uZSIpewogICMgdGhpcyBmdW5jdGlvbiB1c2VzIG15X2NvbXBfcmVnaW9uX2VmZmVjdCB0byBnZW5lcmF0ZSB0aGUgZGF0YQogICMgYW5kIHBsb3QgdGhlIGRpZmZlcmVuY2UgaW4gQV9QSE8yIGFuZCBBX3BobzIgYmV0d2VlbiB0aGUgQ2dQaG80IHZzIFNjUGhvNAogICMgaW4gdGhlIGZvY2FsIHJlZ2lvbgogIGRhdCA8LSBteV9jb21wX3JlZ2lvbl9lZmZlY3QocmVnaW9uKSAlPiUgCiAgICBwaXZvdF9sb25nZXIoY29scyA9IGMoZEFfUEhPMiwgZEFfcGhvMiksIAogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gImhvc3QiLCB2YWx1ZXNfdG8gPSAiZGlmZiIpICU+JSAKICAgIG11dGF0ZShob3N0ID0gZmN0X3JlY29kZShob3N0LCBgUEhPMmAgPSAiZEFfUEhPMiIsIGBwaG8y4oiGYCA9ICJkQV9waG8yIiksCiAgICAgICAgICAgaG9zdCA9IGZjdF9yZWxldmVsKGhvc3QsICJQSE8yIikpCiAgaWYoaGlnaGxpZ2h0ICE9ICJub25lIiAmIGhpZ2hsaWdodCAhPSByZWdpb24pewogICAgaGwgPSBhcy5udW1lcmljKGhpZ2hsaWdodCkKICAgIGRhdCA8LSBtdXRhdGUoZGF0LCBncnAgPSBzdHJfc3ViKGJnLCBobCwgaGwpICU+JSB0b3VwcGVyKCksCiAgICAgICAgICAgICAgICAgIGdycCA9IGZjdF9yZWNvZGUoZ3JwLCBDZ1BobzQgPSAiQyIsIFNjUGhvNCA9ICJTIikpCiAgfWVsc2V7CiAgICBkYXQgPC0gbXV0YXRlKGRhdCwgZ3JwID0gaWZlbHNlKE5GLCAibi5mLiIsICJvdGhlcnMiKSkKICB9CiAgIyBzcGVjaWZ5IGxlZ2VuZCB0aXRsZQogIGxlZ2VuZC50aXRsZSA9ICIiCiAgaWYoaGlnaGxpZ2h0ICE9ICJub25lIiAmIGhpZ2hsaWdodCAhPSByZWdpb24pewogICAgaGwgPSBhcy5udW1lcmljKGhpZ2hsaWdodCkKICAgIGRhdCA8LSBtdXRhdGUoZGF0LCBncnAgPSBzdHJfc3ViKGJnLCBobCwgaGwpICU+JSB0b3VwcGVyKCksCiAgICAgICAgICAgICAgICAgIGdycCA9IGZjdF9yZWNvZGUoZ3JwLCBDZ1BobzQgPSAiQyIsIFNjUGhvNCA9ICJTIikpCiAgICBsZWdlbmQudGl0bGUgPSBwYXN0ZSgiUmVnaW9uIiwgaGlnaGxpZ2h0LCBzZXAgPSAiICIpCiAgfWVsc2V7CiAgICBkYXQgPC0gbXV0YXRlKGRhdCwgZ3JwID0gaWZlbHNlKE5GLCAibm8iLCAieWVzIikpCiAgICBsZWdlbmQudGl0bGUgPSAiRnVuY3Rpb25hbCIKICB9CiAgIyBzcGVjaWZ5IGFycm93IGFubm90YXRpb24KICBhcnJvdy54ID0gMC43CiAgYXJyb3cueSA9IChtYXgoZGF0JGRpZmYpIC0gbWluKGRhdCRkaWZmKSkgLyA1IAogIHAgPC0gZGF0ICU+JSAKICAgIGdncGxvdChhZXMoeCA9IGhvc3QsIHkgPSBkaWZmLCBsYWJlbCA9IGJnKSkgKwogICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBncnApLCBzaXplID0gMiwgYWxwaGEgPSAwLjgsCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKDAuMDUpKSArIAogICAgZ2VvbV9saW5lKGFlcyhncm91cCA9IGJnKSwgbGluZXdpZHRoID0gMC4yLCBhbHBoYSA9IDAuOCkgKwogICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gYXJyb3cueCwgeGVuZCA9IGFycm93LngsIHkgPSAtYXJyb3cueSwgeWVuZCA9IGFycm93LnkpLAogICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjAzLCAibnBjIiksIGVuZHMgPSAiYm90aCIpLCAKICAgICAgICAgICAgICAgICBjb2xvciA9ICJncmF5NjAiLCBsd2QgPSAxLCBhbHBoYSA9IDAuNSkgKwogICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gYXJyb3cueCAtIDAuMDUsIHhlbmQgPSBhcnJvdy54ICsgMC4wNSwgeSA9IDAsIHllbmQgPSAwKSwKICAgICAgICAgICAgICAgICBsd2QgPSAyLCBjb2xvciA9ICJncmF5NjAiKSArCiAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSBhcnJvdy54IC0gMC4xLCB5ID0gNSwgbGFiZWwgPSAiQ2dQaG80KysiLCAKICAgICAgICAgICAgIGFuZ2xlID0gJzkwJywgY29sb3IgPSAiZ3JheTMwIikgKwogICAgYW5ub3RhdGUoInRleHQiLCB4ID0gYXJyb3cueCArIDAuMSwgeSA9IC01LCBsYWJlbCA9ICJTY1BobzQrKyIsIAogICAgICAgICAgICAgYW5nbGUgPSAnMjcwJywgY29sb3IgPSAiZ3JheTMwIikgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKGxlZ2VuZC50aXRsZSwgdmFsdWVzID0gYygib3JhbmdlIiwgImdyYXkyMCIpKSArCiAgICB5bGFiKCJSZWdpb24gc3dhcCBlZmZlY3QgKENnLVNjKSIpICsKICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE4KSArIAogICAgdGhlbWUoCiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC45KSksCiAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC44KSksCiAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuOSkpLAogICAgKQogIHJldHVybihwKQp9CgpteV9wbG90X3JlZ2lvbl9lZmZlY3RfdHdvdmFyX2xpbmUoIjQiLCAiNSIpIyAlPiUgZ2dwbG90bHkoKQpnZ3NhdmUoIi4uL2ltZy8yMDIzMTIyMS1yZWdpb24tc3dhcC1lZmZlY3QtNC1vbi01LnBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCwgZHBpID0gMTUwKQpgYGAKCmBgYHtyfQpteV9wbG90X3JlZ2lvbl9lZmZlY3RfdHdvdmFyX2xpbmUoIjUiLCAiNCIpIyAlPiUgZ2dwbG90bHkoKQpnZ3NhdmUoIi4uL2ltZy8yMDIzMTIyNC1yZWdpb24tc3dhcC1lZmZlY3QtNS1vbi00LnBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCwgZHBpID0gMjAwKQpgYGAKCmBgYHtyfQpteV9wbG90X3JlZ2lvbl9lZmZlY3RfdHdvdmFyX2xpbmVfcGFyIDwtIGZ1bmN0aW9uKHJlZ2lvbnMsIGhpZ2hsaWdodCA9ICJub25lIil7CiAgIyB0aGlzIGZ1bmN0aW9uIHVzZXMgbXlfY29tcF9yZWdpb25fZWZmZWN0IHRvIGdlbmVyYXRlIHRoZSBkYXRhCiAgIyBhbmQgcGxvdCB0aGUgZGlmZmVyZW5jZSBpbiBBX1BITzIgYW5kIEFfcGhvMiBiZXR3ZWVuIHRoZSBDZ1BobzQgdnMgU2NQaG80CiAgIyBpbiB0aGUgZm9jYWwgcmVnaW9uCiAgZGF0IDwtIG1hcF9kZnIocmVnaW9ucywgXChyZWdpb24pIG15X2NvbXBfcmVnaW9uX2VmZmVjdChyZWdpb24pLCAuaWQgPSAicmVnaW9uIikgJT4lIAogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKGRBX1BITzIsIGRBX3BobzIpLCAKICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJob3N0IiwgdmFsdWVzX3RvID0gImRpZmYiKSAlPiUgCiAgICBtdXRhdGUoaG9zdCA9IGZjdF9yZWNvZGUoaG9zdCwgYFBITzJgID0gImRBX1BITzIiLCBgcGhvMuKIhmAgPSAiZEFfcGhvMiIpLAogICAgICAgICAgIGhvc3QgPSBmY3RfcmVsZXZlbChob3N0LCAiUEhPMiIpKQogICMgc3BlY2lmeSBsZWdlbmQgdGl0bGUKICBsZWdlbmQudGl0bGUgPSAiIgogIGlmKGhpZ2hsaWdodCAhPSAibm9uZSIgJiAhaGlnaGxpZ2h0ICVpbiUgcmVnaW9ucyl7CiAgICBobCA9IGFzLm51bWVyaWMoaGlnaGxpZ2h0KQogICAgZGF0IDwtIG11dGF0ZShkYXQsIGdycCA9IHN0cl9zdWIoYmcsIGhsLCBobCkgJT4lIHRvdXBwZXIoKSwKICAgICAgICAgICAgICAgICAgZ3JwID0gZmN0X3JlY29kZShncnAsIENnUGhvNCA9ICJDIiwgU2NQaG80ID0gIlMiKSkKICAgIGxlZ2VuZC50aXRsZSA9IHBhc3RlKCJSZWdpb24iLCBoaWdobGlnaHQsICJmcm9tIiwgc2VwID0gIiAiKQogIH1lbHNlewogICAgZGF0IDwtIG11dGF0ZShkYXQsIGdycCA9IGlmZWxzZShORiwgIm5vIiwgInllcyIpKQogICAgbGVnZW5kLnRpdGxlID0gIkZ1bmN0aW9uYWwiCiAgfQogICMgc3BlY2lmeSBhcnJvdyBhbm5vdGF0aW9uCiAgYXJyb3cueCA9IDAuNwogIGFycm93LnkgPSAobWF4KGRhdCRkaWZmKSAtIG1pbihkYXQkZGlmZikpIC8gNSAKICBwIDwtIGRhdCAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBob3N0LCB5ID0gZGlmZiwgbGFiZWwgPSBiZykpICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZ3JwKSwgc2l6ZSA9IDIsIGFscGhhID0gMC44LAogICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcigwLjEpKSArIAogICAgZ2VvbV9saW5lKGFlcyhncm91cCA9IGJnKSwgbGluZXdpZHRoID0gMC4yLCBhbHBoYSA9IDAuOCkgKwogICAgZmFjZXRfZ3JpZChncnAgfiByZWdpb24sIGxhYmVsbGVyID0gbGFiZWxsZXIoCiAgICAgIGdycCA9IGMoQ2dQaG80ID0gIlAySUQ6Q2dQaG80IiwgU2NQaG80ID0gIlAySUQ6U2NQaG80IiksCiAgICAgIHJlZ2lvbiA9IGxhYmVsX2JvdGgKICAgICkpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCgiUDJJRDoiLCB2YWx1ZXMgPSBjKCJvcmFuZ2UiLCAiZ3JheTIwIikpICsKICAgIHlsYWIoIlJlZ2lvbiBzd2FwIGVmZmVjdCAoQ2ctU2MpIikgKwogICAgdGhlbWVfYncoYmFzZV9zaXplID0gMTgpICsgCiAgICB0aGVtZSgKICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjkpKSwKICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjgpKSwKICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC45KSksCiAgICApCiAgcmV0dXJuKHApCn0KbXlfcGxvdF9yZWdpb25fZWZmZWN0X3R3b3Zhcl9saW5lX3BhcihjKDEsMiwzKSwgIjQiKQpnZ3NhdmUoIi4uL2ltZy8yMDIzMTIyNC1yZWdpb24tc3dhcC1lZmZlY3QtMXRvMy1vbi00LnBuZyIpCmBgYAoKYGBge3J9Cm15X3Bsb3RfcmVnaW9uX2VmZmVjdF90d292YXJfc2lkZSA8LSBmdW5jdGlvbihyZWdpb24pewogICMgdGhpcyBmdW5jdGlvbiB1c2VzIG15X2NvbXBfcmVnaW9uX2VmZmVjdCB0byBnZW5lcmF0ZSB0aGUgZGF0YQogICMgYW5kIHBsb3QgdGhlIGRpZmZlcmVuY2UgaW4gQV9QSE8yIGFuZCBBX3BobzIgYmV0d2VlbiB0aGUgQ2dQaG80IHZzIFNjUGhvNAogICMgaW4gdGhlIGZvY2FsIHJlZ2lvbgogIGRhdCA8LSBteV9jb21wX3JlZ2lvbl9lZmZlY3QocmVnaW9uKSAlPiUgCiAgICBwaXZvdF9sb25nZXIoY29scyA9IGMoZEFfUEhPMiwgZEFfcGhvMiksIAogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gImhvc3QiLCB2YWx1ZXNfdG8gPSAiZGlmZiIpICU+JSAKICAgIG11dGF0ZShob3N0ID0gZmN0X3JlY29kZShob3N0LCBgUEhPMmAgPSAiZEFfUEhPMiIsIGBwaG8y4oiGYCA9ICJkQV9waG8yIiksCiAgICAgICAgICAgaG9zdCA9IGZjdF9yZWxldmVsKGhvc3QsICJQSE8yIikpCiAgcCA8LSBkYXQgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gYmcsIHkgPSBkaWZmLCBncm91cCA9IGhvc3QpKSArCiAgICBnZW9tX2NvbChhZXMoZmlsbCA9IGhvc3QpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSkpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGhvc3QuY29sb3JzKSArCiAgICB5bGFiKCJSZWdpb24gc3dhcCBkaWZmIChDZyB2cyBTYykiKSArCiAgICB0aGVtZV9jb3dwbG90KGZvbnRfc2l6ZSA9IDIwKSArIAogICAgcGFuZWxfYm9yZGVyKGNvbG9yID0gImdyYXkzMCIpICsKICAgIGJhY2tncm91bmRfZ3JpZChtYWpvciA9ICJ5IiwgbWlub3IgPSAibm9uZSIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgZmFtaWx5ID0gImNvdXJpZXIiKSwKICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQogIHJldHVybihwKQp9CmBgYAoKIyMgVHJpYW5nbGUgaGVhdG1hcApGaXJzdCwgd3JpdGUgYSBmdW5jdGlvbiB0byBnZW5lcmF0ZSB0aGUgZGF0YSBmb3IgcGxvdHRpbmcuIElmIHdlIGFyZSBnb2luZyB0byB1c2UgZ2dwbG90LCB3ZSBuZWVkIGEgdGliYmxlIHRvIHN0b3JlIHRoZSBkYXRhLCBzb21ldGhpbmcgaW4gdGhlIGZvbGxvd2luZyBmb3JtCgp8IHBsYXNtaWQgfCBzeW1ib2wgfCBSZWdpb25BIHwgUmVnaW9uQiB8IEFfUEhPMiB8IEFfcGhvMiB8IHNfUEhPMiB8IGJvb3N0IHwgcGVyY19waG8yIHwKfDotLS0tLS0tIHw6LS0tLS0tIHw6LS0tLS0tLSB8Oi0tLS0tLS0gfDotLS0tLS0gfDotLS0tLS0gfDotLS0tLS0gfDotLS0tLSB8Oi0tLS0tLS0tLSB8CnwgMjA5ICAgICB8IENDU0NDICB8IDMgICAgICAgfCAzICAgICAgIHwgOC4yNSAgIHwgNy44MiAgIHwgMC40NjggIHwgMS4wNiAgfCAwLjk0ICAgICAgfAoKSWYgd2UgYXJlIG9rIHdpdGggdXNpbmcgbm9uIGdncGxvdCAtIGhlYXRtYXBzIGFyZSBub3QgZ2dwbG90J3Mgc3RyZW5ndGggYW55d2F5cyAtIHdlIGNhbiBqdXN0IGJ1aWxkIGEgbWF0cml4LgoKTm90ZSB0aGF0IHRoaXMgd2F5IG9mIHN1bW1hcml6aW5nIHRoZSBkYXRhIGhhcyBtYW55IGxpbWl0YWl0b25zOiAxKSBpdCByZXF1aXJlcyBzcGVjaWZ5aW5nIHRoZSByZWZlcmVuY2UsIGVpdGhlciBDQ0NDQyBvciBTU1NTUy4gRXZlcnl0aGluZyBpcyBtZWFzdXJlZCBhZ2FpbnN0IHRoYXQ7IDIpIGl0IG9ubHkgc2hvd3MgcGFpcndpc2UgKHR3byByZWdpb24pIGludGVyYWN0aW9ucy4gVGhpcyB0dXJucyBvdXQgdG8gYmUgZmluZSB3aXRoIGZpdmUgcmVnaW9ucywgc2luY2UgZXZlcnkgY2hpbWVyYSBjYW4gYmUgZXhwcmVzc2VkIGFzIGVpdGhlciBhIDAsIDEgb3IgMiByZWdpb24gc3dhcCBmcm9tIG9uZSBvZiB0aGUgdHdvIHJlZmVyZW5jZSBnZW5vdHlwZXMuIFdpdGggNiBvciBtb3JlIHJlZ2lvbnMsIGhpZ2hlciBsZXZlbCAoMyBvciBtb3JlIHJlZ2lvbikgaW50ZXJhY3Rpb25zIGNhbm5vdCBiZSB2aXN1YWxpemVkIHRoaXMgd2F5LiBCZWNhdXNlIG9mIHRoaXMsIHdlIHdpbGwgZm9jdXMgb24ganVzdCB0aGUgbWFpbiBzZXQgZm9yIHRoaXMgYW5hbHlzaXMuCgpUbyBidWlsZCB0aGUgbWF0cml4LCB3ZSBuZWVkIHRvIGZpcnN0IGlkZW50aWZ5IHRoZSBjaGltZXJhcyB0aGF0IGJlbG9uZyB0byB0aGUgc2V0LiBGb3IgdGhhdCwgd2Ugd2lsbCB1c2UgdGhlICJtYWluIiBzZXQsIHdpdGggdGhlIGZpdmUgcmVnaW9uIHNwbGl0LCBmb3IgdGhlIG1vbWVudCBhdCBsZWFzdC4gVGhlIGZ1bmN0aW9uIHdpbGwgZmlyc3QgZGV0ZXJtaW5lIHdoaWNoIHJlZmVyZW5jZSB0byB1c2UuIElmIHdlIHVzZSBTU1NTUyBhcyB0aGUgcmVmZXJlbmNlLCBmb3IgZXhhbXBsZSwgd2Ugd2lsbCBhc3NpZ24gMCB0byB0aGUgcmVmZXJlbmNlLiBBbGwgb3RoZXIgY2hpbWVyYXMgd2l0aCAxIG9yIDIgcmVnaW9ucyBmcm9tIENnIHdpbGwgYmUgdXNlZCB0byBmaWxsIGFuIHVwcGVyIHRyaWFuZ3VsYXIgbWF0cml4LCB1c2luZyBvbmUgb2YgdGhlIHZhbHVlcyBvZiBpbnRlcmVzdCwgZS5nLiwgQV9QSE8yLgpgYGB7cn0KbXlfdXBwZXJfdHJpYW5ndWxhcl9tYXQgPC0gZnVuY3Rpb24oYWx0ID0gIkMiLCB2YXIgPSAiQV9QSE8yIiwgbmYuYXMubmEgPSBGKXsKICAjIGdpdmVuIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGUgKEMvUykgYW5kIGEgdmFyaWFibGUgb2YgaW50ZXJlc3QsIGUuZy4sIEFfUEhPMiwKICAjIG91dHB1dCBhbiB1cHBlciB0cmlhbmd1bGFyIG1hdHJpeCBjb250YWluaW5nIHRoZSB2YWx1ZXMgZnJvbSB0aGUgdmFyaWFibGUgCiAgIyBvZiBpbnRlcmVzdCwgd2l0aCB0aGUgcm93IGFuZCBjb2wgbnVtYmVycyBiYXNlZCBvbiB0aGUgZmlyc3QgYW5kIHNlY29uZAogICMgcG9zaXRpb25zIGNvbnRhaW5pbmcgdGhlIGFsdGVybmF0aXZlIGFsbGVsZS4gSWYgYWxsIHBvc2l0aW9ucyBjb250YWluIHRoZSAKICAjIHJlZmVyZW5jZSBhbGxlbGUsIHRoZSB2YWx1ZSBpcyBzdWJ0cmFjdGVkIGZyb20gYWxsIHZhbHVlcyBpbiB0aGUgbWF0cml4CiAgIyB3aGVuIGp1c3Qgb25lIHBvc2l0aW9uIGlzIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGUsIHRoZSB2YWx1ZSBpbiB0aGUgZGlhZ29uYWwKICAjIGlzIHNldC4gd2hlbiB0aGVyZSBhcmUgbW9yZSB0aGFuIDIgcmVnaW9ucyBjb250YWluaW5nIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGUKICAjIHNraXAuCiAgIyBpZiAibmYuYXMubmEgPSBUUlVFIiwgZXZhbHVhdGUgaWYgdGhlIGFjdGl2aXR5IG9mIGVpdGhlciBvZiB0aGUgdHdvIGNoaW1lcmFzCiAgIyBiZWluZyBjb21wYXJlZCBpcyBub24gZnVuY3Rpb25hbC4gaWYgeWVzLCBzZXQgdGhlIGNvcnJlc3BvbmRpbmcgbWF0cml4IHZhbHVlCiAgIyB0byBOQQogIG91dF9tYXQgPC0gbWF0cml4KE5BLCBucm93ID0gNSwgbmNvbCA9IDUpCiAgcmVmX3ZhbCA8LSBOQQogIGRhdCA8LSBmaWx0ZXIoeGltZXJhLCBzZXQgPT0gIk0iKSAlPiUgCiAgICBtdXRhdGUoUyA9IGFzLmNoYXJhY3RlcihzeW1ib2wpICU+JSB0b3VwcGVyKCkpCiAgaWYobmYuYXMubmEpewogICAgZGF0IDwtIGZpbHRlcihkYXQsIGdyb3VwICE9ICJuLmYuIikKICB9CiAgZm9yKGkgaW4gc2VxKDEsIG5yb3coZGF0KSkpewogICAgc3ltYm9sID0gZGF0W2ksICJTIl0KICAgICMgZGV0ZXJtaW5lIHdoaWNoIHBvc2l0aW9ucyBjb250YWluIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGUKICAgIHAgPSBzdHJfbG9jYXRlX2FsbChzeW1ib2wsIGFsdClbWzFdXVssInN0YXJ0Il0KICAgIGwgPSBsZW5ndGgocCkgICAjIGhvdyBtYW55IHBvc2l0aW9ucyBjb250YWluIHRoZSBhbHQgYWxsZWxlCiAgICB2ID0gZGF0W1t2YXJdXVtpXSAjIHJldHJpZXZlIHRoZSB2YWx1ZSBvZiB0aGUgdmFyaWFibGUKICAgIGlmKGwgPT0gMCkKICAgICAgcmVmX3ZhbCA9IHYKICAgIGVsc2UgaWYobCA9PSAxKQogICAgICBvdXRfbWF0W3AsIHBdID0gdgogICAgZWxzZSBpZihsID09IDIpCiAgICAgIG91dF9tYXRbcFsxXSwgcFsyXV0gPSB2CiAgfQogIG91dF9tYXQgPSBvdXRfbWF0IC0gcmVmX3ZhbAogIHJldHVybihvdXRfbWF0KQp9CmBgYApgYGB7cn0KbXlfY29tYmluZWRfdHJpYW5ndWxhcl9tYXQgPC0gZnVuY3Rpb24oYWx0ID0gIkMiKXsKICAjIGdpdmVuIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGUgKEMvUyksIG91dHB1dCBhIG1hdHJpeCBjb250YWluaW5nIHRoZSB2YWx1ZXMKICAjIGZvciBib3RoIHdpdGggYW5kIHdpdGhvdXQgUGhvMiwgYXJyYW5nZWQgaW4gdHdvIGNvbXBsZW1lbnRhcnkgdHJpYWd1bGFyCiAgIyBtYXRyaWNlcywgd2l0aCB0aGUgcm93IGFuZCBjb2wgbnVtYmVycyBiYXNlZCBvbiB0aGUgZmlyc3QgYW5kIHNlY29uZAogICMgcG9zaXRpb25zIGNvbnRhaW5pbmcgdGhlIGFsdGVybmF0aXZlIGFsbGVsZS4gSWYgYWxsIHBvc2l0aW9ucyBjb250YWluIHRoZSAKICAjIHJlZmVyZW5jZSBhbGxlbGUsIHRoZSB2YWx1ZSBpcyBzdWJ0cmFjdGVkIGZyb20gYWxsIHZhbHVlcyBpbiB0aGUgbWF0cml4CiAgIyB3aGVuIGp1c3Qgb25lIHBvc2l0aW9uIGlzIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGUsIHRoZSB2YWx1ZSBpbiB0aGUgZGlhZ29uYWwKICAjIGlzIHNldC4gd2hlbiB0aGVyZSBhcmUgbW9yZSB0aGFuIDIgcmVnaW9ucyBjb250YWluaW5nIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGUKICAjIHNraXAuCiAgb3V0X21hdCA8LSBtYXRyaXgoTkEsIG5yb3cgPSA2LCBuY29sID0gNikKICB1cHBlciA8LSBjYmluZChOQSwgbXlfdXBwZXJfdHJpYW5ndWxhcl9tYXQoYWx0LCB2YXIgPSAiQV9QSE8yIiwgKSkgJT4lIAogICAgcmJpbmQoLiwgTkEpCiAgbG93ZXIgPC0gcmJpbmQoTkEsIHQobXlfdXBwZXJfdHJpYW5ndWxhcl9tYXQoYWx0LCB2YXIgPSAiQV9waG8yIikpKSAlPiUgCiAgICBjYmluZCguLCBOQSkKICBvdXRfbWF0ID0gaWZlbHNlKGlzLm5hKHVwcGVyKSwgbG93ZXIsIHVwcGVyKQogIHJldHVybihvdXRfbWF0KQp9CmBgYAoKYGBge3J9Cm15X3Bsb3RfdHJpYW5nbGVfaGVhdG1hcCA8LSBmdW5jdGlvbihhbHQsIHZhcil7CiAgIyB0aGlzIGZ1bmN0aW9uIHRha2VzIHRoZSBvdXRwdXQgb2YgdGhlIGZ1bmN0aW9uIGFib3ZlIGFuZCBtYWtlcyBhIGhlYXRtYXAKICAjIHVzaW5nIHBoZWF0bWFwIGZ1bmN0aW9uLCB0aGVuIHJvdGF0ZXMgaXQgdXNpbmcgZ3JpZCBncmFwaGljcwogICMgdGhhbmtzIHRvIGh0dHBzOi8vYm9va2Rvd24ub3JnL3JkcGVuZy9SUHJvZ0RBL3RoZS1ncmlkLXBhY2thZ2UuaHRtbCNncmlkLWdyYXBoaWNzLWNvb3JkaW5hdGUtc3lzdGVtcwogICMgYWRkaW5nIHRpdGxlIGJhc2VkIG9uIGh0dHBzOi8vZGF2ZXRhbmcuZ2l0aHViLmlvL211c2UvcGhlYXRtYXAuaHRtbAogIAogICMgY29uc3RydWN0IHRpdGxlIG9mIHBsb3QKICByZWYgPSBpZmVsc2UoYWx0ID09ICJDIiwgIlNjUGhvNCIsICJDZ1BobzQiKQogIGJnID0gaWZlbHNlKHZhciA9PSAiQV9QSE8yIiwgIndpdGggUEhPMiIsICJ3L28gcGhvMiIpCiAgbXlfdGl0bGUgPC0gcGFzdGUoIkVwaXN0YXNpcyBiZXR3ZWVuIHJlZ2lvbnMgb24iLCByZWYsICJiYWNrZ3JvdW5kIiwgYmcpCiAgdGVzdCA8LSBteV91cHBlcl90cmlhbmd1bGFyX21hdChhbHQgPSBhbHQsIHZhciA9IHZhcikKICBwYWxldHRlTGVuZ3RoID0gNTAKICBteUNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKGMoInN0ZWVsYmx1ZTQiLCAiZ3JheTkwIiwgInJlZCIpKShwYWxldHRlTGVuZ3RoKQogIHJuZyA8LSBtYXgoYWJzKHRlc3QpLCBuYS5ybSA9IFRSVUUpCiAgbXlCcmVha3MgPC0gYyhzZXEoLXJuZywgMCwgbGVuZ3RoLm91dD1jZWlsaW5nKHBhbGV0dGVMZW5ndGgvMikgKyAxKSwgCiAgICAgICAgICAgICAgICBzZXEocm5nL3BhbGV0dGVMZW5ndGgsIHJuZywKICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0PWZsb29yKHBhbGV0dGVMZW5ndGgvMikpKQogIHAgPC0gcGhlYXRtYXA6OnBoZWF0bWFwKHRlc3QsIGNvbG9yID0gbXlDb2xvcnMsIGJyZWFrcyA9IG15QnJlYWtzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLCBuYV9jb2wgPSBOQSwgc2lsZW50ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGQUxTRSwgY2x1c3Rlcl9yb3dzID0gRkFMU0UpCiAgdnAgPC0gdmlld3BvcnQoeCA9IDAuNSwgeSA9IDAuMjUsCiAgICAgICAgICAgICAgICAgd2lkdGggPSB1bml0KDQuNSwgImluIiksIGhlaWdodCA9IHVuaXQoNC41LCAiaW4iKSwgYW5nbGUgPSA0NykgCiAgZ3JpZC5uZXdwYWdlKCkKICBwdXNoVmlld3BvcnQodnApCiAgZ3JpZC5kcmF3KHAkZ3RhYmxlKQogIHBvcFZpZXdwb3J0KCkKICBncmlkLnRleHQobGFiZWwgPSBteV90aXRsZSwgeCA9IDAuNSwgeSA9IDAuOTUsIGdwID0gZ3Bhcihmb250c2l6ZSA9IDE2LCBmb250ZmFjZSA9ICJib2xkIikpCiAgcmV0dXJuKHApCn0KYGBgCmBgYHtyfQpteV9wbG90X2NvbWJpbmVkX3RyaWFuZ2xlX2hlYXRtYXAgPC0gZnVuY3Rpb24oYWx0KXsKICAjIHRoaXMgZnVuY3Rpb24gdGFrZXMgdGhlIG91dHB1dCBvZiB0aGUgZnVuY3Rpb24gbXlfY29tYmluZWRfdHJpYW5ndWxhcl9tYXQoKQogICMgdXNpbmcgcGhlYXRtYXAgZnVuY3Rpb24sIHRoZW4gcm90YXRlcyBpdCB1c2luZyBncmlkIGdyYXBoaWNzCiAgIyB0aGFua3MgdG8gaHR0cHM6Ly9ib29rZG93bi5vcmcvcmRwZW5nL1JQcm9nREEvdGhlLWdyaWQtcGFja2FnZS5odG1sI2dyaWQtZ3JhcGhpY3MtY29vcmRpbmF0ZS1zeXN0ZW1zCiAgIyBhZGRpbmcgdGl0bGUgYmFzZWQgb24gaHR0cHM6Ly9kYXZldGFuZy5naXRodWIuaW8vbXVzZS9waGVhdG1hcC5odG1sCiAgCiAgIyBjb25zdHJ1Y3QgdGl0bGUgb2YgcGxvdAogIHJlZiA9IGlmZWxzZShhbHQgPT0gIkMiLCAiU2NQaG80IiwgIkNnUGhvNCIpCiAgbXlfdGl0bGUgPC0gcGFzdGUoIkVwaXN0YXNpcyBiZXR3ZWVuIHJlZ2lvbnMgb24iLCByZWYsICJiYWNrZ3JvdW5kIikKICB0ZXN0IDwtIG15X2NvbWJpbmVkX3RyaWFuZ3VsYXJfbWF0KGFsdCA9IGFsdCkKICBwYWxldHRlTGVuZ3RoID0gNTAKICBteUNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKGMoInN0ZWVsYmx1ZTQiLCAiZ3JheTkwIiwgInJlZCIpKShwYWxldHRlTGVuZ3RoKQogIHJuZyA8LSBtYXgoYWJzKHRlc3QpLCBuYS5ybSA9IFRSVUUpCiAgbXlCcmVha3MgPC0gYyhzZXEoLXJuZywgMCwgbGVuZ3RoLm91dD1jZWlsaW5nKHBhbGV0dGVMZW5ndGgvMikgKyAxKSwgCiAgICAgICAgICAgICAgICBzZXEocm5nL3BhbGV0dGVMZW5ndGgsIHJuZywKICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0PWZsb29yKHBhbGV0dGVMZW5ndGgvMikpKQogIHAgPC0gcGhlYXRtYXA6OnBoZWF0bWFwKHRlc3QsIGNvbG9yID0gbXlDb2xvcnMsIGJyZWFrcyA9IG15QnJlYWtzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLCBuYV9jb2wgPSBOQSwgc2lsZW50ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGQUxTRSwgY2x1c3Rlcl9yb3dzID0gRkFMU0UpCiAgdnAgPC0gdmlld3BvcnQoeCA9IDAuNSwgeSA9IDAuNDUsCiAgICAgICAgICAgICAgICAgd2lkdGggPSB1bml0KDMsICJpbiIpLCBoZWlnaHQgPSB1bml0KDIuOCwgImluIiksIGFuZ2xlID0gNDcpIAogIGdyaWQubmV3cGFnZSgpCiAgcHVzaFZpZXdwb3J0KHZwKQogIGdyaWQuZHJhdyhwJGd0YWJsZSkKICBwb3BWaWV3cG9ydCgpCiAgZ3JpZC50ZXh0KGxhYmVsID0gbXlfdGl0bGUsIHggPSAwLjUsIHkgPSAwLjk1LCAKICAgICAgICAgICAgZ3AgPSBncGFyKGZvbnRzaXplID0gMTYsIGZvbnRmYWNlID0gImJvbGQiKSkKICBncmlkLnRleHQobGFiZWwgPSAiV2l0aCBQaG8yIiwgeCA9IDAuMSwgeSA9IDAuNjUsIGp1c3QgPSBjKCJsZWZ0IiwgInRvcCIpLAogICAgICAgICAgICBncCA9IGdwYXIoZm9udHNpemUgPSAxNCwgZm9udGZhY2UgPSAiYm9sZCIpKQogIGdyaWQudGV4dChsYWJlbCA9ICJXaXRob3V0IHBobzIiLCB4ID0gMC4xLCB5ID0gMC4yNSwganVzdCA9IGMoImxlZnQiLCAidG9wIiksIAogICAgICAgICAgICBncCA9IGdwYXIoZm9udHNpemUgPSAxNCwgZm9udGZhY2UgPSAiYm9sZCIpKQogIHJldHVybihwKQp9CmBgYAoKYGBge3J9CnAxIDwtIG15X3Bsb3RfY29tYmluZWRfdHJpYW5nbGVfaGVhdG1hcCgiQyIpCnAyIDwtIG15X3Bsb3RfY29tYmluZWRfdHJpYW5nbGVfaGVhdG1hcCgiUyIpCmBgYAoKIyMgUmVnaW9uIDQgc3BsaXRzCldlIGhhdmUgc28gZmFyIGZvY3VzZWQgb24gdGhlIG1haW4gc2V0IHdpdGggdGhlIDUgcmVnaW9uIGRlc2lnbi4gSW4gdGhlIHNjYXR0ZXIgcGxvdCBhYm92ZSwgd2Ugc2VlIHRoYXQgdGhlcmUgaXMgYSBzdWJzZXQgb2YgY2hpbWVyYXMgaW4gYmV0d2VlbiB0aGUgUDJJRDpTYyBhbmQgUDJJRDpDZyBvbmVzLiBUaGV5IGFyZSBpbnRlcmVzdGluZyBpbiB0aGF0IHRoZWlyIEFfcGhvMuKIhi9BX1BITzIgcmF0aW9zIGFyZSBpbnRlcm1lZGlhdGUuCiFbc2NhdHRlcl0oLi4vaW1nLzIwMjMxMjIwLWFsbC1jaGltZXJhLXNjYXR0ZXItY29sb3ItYnktUDJJRC5wbmcpCkJlbG93LCB3ZSB3aWxsIHpvb20gaW4gb250byB0aGlzIHNldCBhbmQgdHJ5IHRvIHVuZGVyc3RhbmQgd2hhdCB0aGV5IHRlbGwgdXMuCmBgYHtyfQpteV9wbG90X2Jvb3N0KHNlbGVjdGlvbiA9IHhpbWVyYSRwbGFzbWlkW3hpbWVyYSRzZXQgPT0gIlMiXSkKbXlfcGxvdF9yYXRpbyhzZWxlY3Rpb24gPSB4aW1lcmEkcGxhc21pZFt4aW1lcmEkc2V0ID09ICJTIl0pCmBgYAoK